将<Func <T,bool >>作为参数传入的Moq'ing方法

我对unit testing和嘲笑很新! 我试图写一些unit testing,涵盖了一些与数据存储交互的代码。 数据访问由IRepository封装:

interface IRepository<T> { .... IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate); .... } 

我正在尝试使用IRepository的具体IoC实现来testing的代码如下所示:

 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(); } } 

所以我testingSignupLogic.AddNewCompany()本身的逻辑,而不是逻辑和具体的存储库,我嘲笑IRepository并将其传递到SignupLogic。 嘲笑了仓库看起来像这样:

 Mock<Repository> repoMock = new Mock<Repository>(); repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc").... 

它将返回包含名称设置为“Company Inc”的Company对象的内存中的IEnumberable。 调用SignupLogic.AddNewCompany的unit testing会创build一个具有重复细节的公司,并尝试将其传入,并声明引发了一个ArgumentException,并显示消息“Company already exists”。 这个testing没有通过。

在运行时通过unit testing和AddNewCompany()进行debugging,看起来existingCompany总是为空。 无奈之下,我发现如果我更新SignupLogic.AddNewCompany(),以便对FindBy的调用如下所示:

 Company existingCompany = this.repo.FindBy(c => c.Name == "Company Inc").FirstOrDefault(); 

testing通过,这向我暗示,Moq只响应与我在testing夹具中设置的代码完全相同的代码。 显然,在testing任何重复的公司被SignupLogic.AddNewCompany拒绝时,这并不是特别有用。

我已经尝试设置moq.FindBy(…)使用“Is.ItAny”,但是这不会导致testing通过。

从我正在阅读的所有内容来看,似乎我正在尝试的expression式testing实际上并不适用于Moq。 可能吗? 请帮忙!

只有具有完全相同的结构(和文字值)的Expression才能匹配是正确的。 我build议你使用Returns()的重载,它允许你使用模拟参数的参数:

 repoMock.Setup(moq => moq.FindBy(It.IsAny<Expression<Func<Company, bool>>>()) .Returns((Expression<Func<Company, bool>> predicate) => ...); 

... ,您可以使用predicate返回匹配的公司(如果匹配公司不符合您的预期,甚至可能会抛出exception)。 不是很漂亮,但我认为它会起作用。

你应该可以使用It.IsAny<>()来完成你正在做的事情。 通过使用It.IsAny<>()您可以简单地调整设置的返回types,以testing代码的每个分支。

 It.IsAny<Expression<Func<Company, bool>>>() 

第一个testing,返回一个公司,不pipe谓词哪个会导致exception抛出:

 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. 

第二次testing,使返回types一个空列表会引起添加被调用。

 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()); 

通常你只是嘲笑你自己的types。 那些你不拥有的,真的不应该因为各种各样的困难而被嘲笑。 所以嘲笑expression式 – 正如你问题的名字所暗示的 – 并不是要走的路。

在Moq框架中。 重要的是把.Returns()函数,否则它不匹配。 所以,如果你还没有这样做,这是你的问题。

 repoMock.Setup(moq => moq.FindBy(c => c.Name == "Company Inc").Returns(....