[英]Moq Expression with Constraint … It.Is<Expression<Func<T, bool>>>
[英]Moq'ing methods where Expression<Func<T, bool>> are passed in as parameters
我对单元测试和嘲笑很新! 我正在尝试编写一些单元测试,其中包含一些与数据存储交互的代码。 数据访问由IRepository封装:
interface IRepository<T> {
....
IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
....
}
我正在尝试使用具体的IoC实现的IRepository来测试的代码如下所示:
public class SignupLogic {
private Repository<Company> repo = new Repository<Company>();
public void AddNewCompany(Company toAdd) {
Company existingCompany = this.repo.FindBy(c => c.Name == toAdd.Name).FirstOrDefault();
if(existingCompany != null) {
throw new ArgumentException("Company already exists");
}
repo.Add(Company);
repo.Save();
}
}
因此,我正在测试SignupLogic.AddNewCompany()本身的逻辑,而不是逻辑和具体的存储库,我正在模拟IRepository并将其传递给SignupLogic。 模拟的存储库看起来像这样:
Mock<Repository> repoMock = new Mock<Repository>();
repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc")....
它返回一个内存中的IEnumberable,其中包含名称设置为“Company Inc”的Company对象。 调用SignupLogic.AddNewCompany的单元测试会设置一个具有重复详细信息和trys的公司来传递它,并且我断言抛出了ArgumentException,并显示消息“公司已存在”。 这个测试没有通过。
在运行时通过单元测试和AddNewCompany()进行调试,似乎existingCompany始终为null。 无奈之下,我发现如果我更新SignupLogic.AddNewCompany()以便对FindBy的调用如下所示:
Company existingCompany = this.repo.FindBy(c => c.Name == "Company Inc").FirstOrDefault();
测试通过,这告诉我,Moq只响应与我在测试夹具中设置的代码完全相同的代码。 显然,在测试任何重复的公司被SignupLogic.AddNewCompany拒绝时,这并不是特别有用。
我已经尝试设置moq.FindBy(...)来使用“Is.ItAny”,但这并不会导致测试通过。
从我正在阅读的所有内容来看,似乎我正在尝试测试表达式实际上并不能在这里使用Moq。 可能吗? 请帮忙!
只有具有完全相同结构(和文字值)的Expression
匹配才可能是正确的。 我建议您使用Returns()
的重载,它允许您使用调用mock的参数:
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())
.Returns((Expression<Func<Company, bool>> predicate) => ...);
在...
,您可以使用predicate
来返回匹配的公司(如果匹配的公司不符合您的预期,甚至可能会抛出异常)。 不是很漂亮,但我认为它会起作用。
您应该能够使用It.IsAny<>()
来完成您要执行的操作。 通过使用It.IsAny<>()
您只需调整设置的返回类型即可测试代码的每个分支。
It.IsAny<Expression<Func<Company, bool>>>()
第一次测试,返回公司而不管谓词会导致异常抛出:
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>{new Company{Name = "Company Inc"}});
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
//Assert the exception was thrown.
第二次测试,使返回类型为空列表将导致添加被调用:
var repoMock = new Mock<IRepository<Company>>();
repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>())).Returns(new List<Company>());
var signupLogic = new SignupLogic(repoMock.Object);
signupLogic.AddNewCompany(new Company {Name = "Company Inc"});
repoMock.Verify(r => r.Add(It.IsAny<Company>()), Times.Once());
您通常只会模拟您拥有的类型。 那些你不拥有的人,真的不应该因各种困难而被嘲笑。 所以嘲弄表达 - 正如你的问题所暗示的那样 - 不是要走的路。
在Moq框架中。 将.Returns()
放在函数中是很重要的,否则就不匹配了。 所以,如果你还没有这样做,那就是你的问题。
repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc").Returns(....
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.