我是单元测试和模拟的新手!我正在尝试编写一些单元测试,其中涉及与数据存储交互的一些代码。数据访问由IRepository封装:

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

我尝试使用IRepository的IoC具体实现来测试的代码如下:
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的单元测试使用重复的详细信息设置了一家公司,并尝试将其传递给我,我断言ArgumentException随消息“公司已存在”引发。该测试未通过。

通过运行单元测试和AddNewCompany()进行调试,似乎现存的公司始终为null。无奈之下,我发现如果我更新SignupLogic.AddNewCompany(),则对FindBy的调用如下所示:
Company existingCompany = this.repo.FindBy(c => c.Name == "Company Inc").FirstOrDefault();

测试通过,这向我表明Moq仅响应与我在测试装置中设置的代码完全相同的代码。显然,这对于测试任何重复的公司被SignupLogic.AddNewCompany拒绝的测试不是特别有用。

我尝试将moq.FindBy(...)设置为使用“Is.ItAny”,但这不会导致测试通过。

从我正在阅读的所有内容看来,在这里尝试测试Expressions实际上不是可以用Moq完成的。是否有可能?请帮忙!

最佳答案

仅具有完全相同的结构(和文字值)的Expression会匹配可能是正确的。我建议您使用Returns()的重载,该重载使您可以使用模拟被调用的参数:

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

...中,您可以使用predicate返回匹配的公司(如果匹配的公司不是您期望的,甚至可能引发异常)。不是很漂亮,但是我认为它将起作用。

10-08 18:50