【问题标题】:Mocking method that takes Func<T>采用 Func<T> 的模拟方法
【发布时间】:2016-06-20 16:08:05
【问题描述】:

我正在尝试在 article 中做示例​​p>

有一个通用存储库,它具有如下所示的 Get 方法。

public virtual TEntity Get(Func<TEntity, Boolean> where)
{
    return DbSet.Where(where).FirstOrDefault();
}

还有一种服务方法,通过用户名和密码来验证用户,使用存储库获取。

public int Authenticate(string username, string password)
{
    var user = _unitOfWork.UserRepository.Get(u => u.UserName == username && u.Password == password);

    if (user != null && user.UserId > 0)
    {
        return user.UserId;
    }
    return 0;
}

文章的作者模拟了这个 get 方法,如下所示。

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))
    .Returns((Func<User, bool> expr) => DataInitializer.GetAllUsers().Where(u => u.UserName == CorrectUserName)
            .FirstOrDefault(u => u.Password == CorrectPassword));

mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))
    .Returns((Func<User, bool> expr) => DataInitializer.GetAllUsers().Where(u => u.UserName == WrongUserName)
            .FirstOrDefault(u => u.Password == WrongPassword));

每次我在测试中调用 Get 方法时,它都会进行第二次模拟设置。并且它不将参数视为用户名和密码。

[Test]
public void AuthenticateTest()
{

    var returnId = _userServices.Authenticate(CorrectUserName, CorrectPassword);
    var firstOrDefault = _users.Where(u => u.UserName == CorrectUserName).FirstOrDefault(u => u.Password == CorrectPassword);
    if (firstOrDefault != null)
        Assert.That(returnId, Is.EqualTo(firstOrDefault.UserId));
}

如何模拟和测试这个 Authenticate 方法。

【问题讨论】:

    标签: c# unit-testing moq


    【解决方案1】:

    您的两个设置都引用相同的方法调用:

    mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>()))
    

    这将为每个通过任何Func&lt;User, bool&gt;Get 调用注册一个模拟方法。这与传递的函数的实际值无关。

    由于两个设置都引用同一个调用,第二个设置将覆盖第一个,因此您实际上只有后一个。

    如果你想根据作为参数传递的值返回不同的东西,你有两个选择:要么更改设置以期望更明确的值(IsAny&lt;T&gt; 除外),要么更改返回函数来实际检查值。例如:

    mock.Setup(x => x.DoSomething(2)).Returns(4);
    mock.Setup(x => x.DoSomething(3)).Returns(9);
    

    或者:

    mock.Setup(x => x.DoSomething(It.IsAny<int>()).Returns(v => {
        if (v == 2)
            return 4;
        else
            return 9;
    });
    

    在您的情况下,您希望根据传递的 lambda 表达式更改返回值。这实际上有点复杂。最简单的可能是提供一个静态用户列表并在其上运行实际过滤器:

    mockRepo.Setup(s => s.Get(It.IsAny<Func<User, bool>>())).Returns(expr => {
        User[] users = new User[] {
            new User { UserName = CorrectUserName, Password = CorrectPassword },
            new User { UserName = WrongUserName, Password = "foobar" },
        };
        return users.Where(expr).FirstOrDefault();
    });
    

    【讨论】:

    • 感谢您的回答。如果我想为我的 Authenticate 方法编写测试,我可以按照你说的做。但是这个测试是针对我在存储库中的 Get 方法的。所以我认为我应该模拟 Get 方法。我错了吗?
    • @YusufDuyar 我确实在我的回答中模拟了Get 方法。但是不,如果你想测试 Get 方法,你不应该模拟它(那时你将测试你的模拟)。如果您想测试Authenticate 方法(它使用 Get 方法),模拟Get 方法在这里很有用。
    • 谢谢@poke。我是单元测试的新手,你的 cmets 对我来说很有解释性:)
    猜你喜欢
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    相关资源
    最近更新 更多