在两种不同的方法上使用 Mock.Setup 与一种方法不匹配,而是 returns null
Using Mock.Setup on two different methods doesn't match on one and instead returns null
我是单元测试的新手。我正在使用 Moq 进行单元测试。我有一种情况,我必须在同一部分模拟两种不同的方法:
我有一个如下所示的操作方法:
public ActionResult Login(someparameters)
{
//code...
var user = userRepository.SelectAllUserByEmail(someparamters); //first method
//....
var userDetails = userRepository.ValidateUser(someparameters);//second method
}
这是我的单元测试部分:
userrepositoryMock.Setup(r => r.SelectAllUserByEmail(someparameters))
.Returns(new List<User>() { new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId } } );
userrepositoryMock.Setup(k => k.ValidateUser(someparamters))
.Returns(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });
但这只会模拟 ValidateUser
的 SelectAllUserByEmail
方法 returns null。
我已经通过添加以下代码解决了这个问题:
userrepositoryMock.SetReturnsDefault(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });
您没有指定 someparameters
代表什么类型,但我敢打赌其中至少有一个是引用类型(除了简单的字符串)。
对于引用类型(如对象实例),在确切的实例上使用 Moq 的 .Setup
通常不是一个好主意,因为这将需要将完全相同的引用传递给模拟的 class为了使安装程序与 return 提供的输出相匹配。
这是一个重现问题的简单 MVCE。给定以下代码:
public class User
{
public string Name { get; set; }
}
public interface IMyInterface
{
string GetUserName(User user);
}
下面的单元测试演示了绑定到特定对象实例 (aUser
) 的 Setup
如果另一个引用 (sameUser
) 传递给 Mock:
[Test]
public void TestGetUserBad()
{
var mock = new Mock<IMyInterface>();
var aUser = new User { Name = "User1" };
var sameUser = new User { Name = "User1" };
mock.Setup(x => x.GetUserName(aUser)).Returns<User>(u => u.Name);
Assert.AreEqual("User1", mock.Object.GetUserName(aUser),
"The mock has been setup for aUser, so this works");
Assert.AreEqual(null, mock.Object.GetUserName(sameUser),
"aUser is a different reference than sameUser hence fails");
}
相反,您应该使用 Moq 的 It.Is<>
(带谓词)或 It.IsAny<>
(任何)matchers 以允许匹配任何满足谓词(如果有)的引用。
[Test]
public void TestGetUserGood()
{
var mock = new Mock<IMyInterface>();
var aUser = new User { Name = "User1" };
var sameUser = new User { Name = "User1" };
mock.Setup(x => x.GetUserName(It.IsAny<User>())).Returns<User>(u => u.Name);
Assert.AreEqual("User1", mock.Object.GetUserName(aUser),
"The mock has been setup for any user, so this works");
Assert.AreEqual("User1", mock.Object.GetUserName(sameUser),
"The mock has been setup for any user, so this works");
}
编辑
出于兴趣,如果您怀疑您的 Mock 设置之一未按预期匹配(因为如果未找到匹配项,则在使用松散模拟时 Moq 将 return default(T)
),您可以临时切换 MockBehaviour to Strict,如果设置不匹配,将抛出。
例如将以下内容应用于 TestGetUserBad
var mock = new Mock<IMyInterface>(MockBehavior.Strict);
结果:
Moq.MockException : IMyInterface.GetUserName(User) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.
我是单元测试的新手。我正在使用 Moq 进行单元测试。我有一种情况,我必须在同一部分模拟两种不同的方法:
我有一个如下所示的操作方法:
public ActionResult Login(someparameters)
{
//code...
var user = userRepository.SelectAllUserByEmail(someparamters); //first method
//....
var userDetails = userRepository.ValidateUser(someparameters);//second method
}
这是我的单元测试部分:
userrepositoryMock.Setup(r => r.SelectAllUserByEmail(someparameters))
.Returns(new List<User>() { new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId } } );
userrepositoryMock.Setup(k => k.ValidateUser(someparamters))
.Returns(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });
但这只会模拟 ValidateUser
的 SelectAllUserByEmail
方法 returns null。
我已经通过添加以下代码解决了这个问题:
userrepositoryMock.SetReturnsDefault(new User { Salt = strSalt, FundraiserAdminId = fundadmind, StatusCode = statusCode, UserTypeId = userTypeId, HomePageURL = homepageURL, OrganizationId = organizationId });
您没有指定 someparameters
代表什么类型,但我敢打赌其中至少有一个是引用类型(除了简单的字符串)。
对于引用类型(如对象实例),在确切的实例上使用 Moq 的 .Setup
通常不是一个好主意,因为这将需要将完全相同的引用传递给模拟的 class为了使安装程序与 return 提供的输出相匹配。
这是一个重现问题的简单 MVCE。给定以下代码:
public class User
{
public string Name { get; set; }
}
public interface IMyInterface
{
string GetUserName(User user);
}
下面的单元测试演示了绑定到特定对象实例 (aUser
) 的 Setup
如果另一个引用 (sameUser
) 传递给 Mock:
[Test]
public void TestGetUserBad()
{
var mock = new Mock<IMyInterface>();
var aUser = new User { Name = "User1" };
var sameUser = new User { Name = "User1" };
mock.Setup(x => x.GetUserName(aUser)).Returns<User>(u => u.Name);
Assert.AreEqual("User1", mock.Object.GetUserName(aUser),
"The mock has been setup for aUser, so this works");
Assert.AreEqual(null, mock.Object.GetUserName(sameUser),
"aUser is a different reference than sameUser hence fails");
}
相反,您应该使用 Moq 的 It.Is<>
(带谓词)或 It.IsAny<>
(任何)matchers 以允许匹配任何满足谓词(如果有)的引用。
[Test]
public void TestGetUserGood()
{
var mock = new Mock<IMyInterface>();
var aUser = new User { Name = "User1" };
var sameUser = new User { Name = "User1" };
mock.Setup(x => x.GetUserName(It.IsAny<User>())).Returns<User>(u => u.Name);
Assert.AreEqual("User1", mock.Object.GetUserName(aUser),
"The mock has been setup for any user, so this works");
Assert.AreEqual("User1", mock.Object.GetUserName(sameUser),
"The mock has been setup for any user, so this works");
}
编辑
出于兴趣,如果您怀疑您的 Mock 设置之一未按预期匹配(因为如果未找到匹配项,则在使用松散模拟时 Moq 将 return default(T)
),您可以临时切换 MockBehaviour to Strict,如果设置不匹配,将抛出。
例如将以下内容应用于 TestGetUserBad
var mock = new Mock<IMyInterface>(MockBehavior.Strict);
结果:
Moq.MockException : IMyInterface.GetUserName(User) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.