有人可以澄清我对模拟验证概念的理解吗?

时间:2011-03-07 08:03:44

标签: .net asp.net mocking moq rhino-mocks

我正在玩一些单元测试和嘲笑。我正在尝试验证我的方法中的某些代码是否已被调用。我认为我不理解嘲弄权的Verify部分,因为我只能验证主要方法..这是愚蠢的,因为这是我Act无论如何。

我正在尝试测试我的逻辑是否正常 - 所以我认为我使用Verify来查看方法中的某些步骤已经达成并已制定。

让我们用这个例子来强调我做错了什么。

public interface IAuthenticationService
{
    bool Authenticate(string username, string password);
    SignOut();
}

public class FormsAuthenticationService : IAuthenticationService
{
    public bool Authenticate(string username, string password)
    {
        var user = _userService.FindSingle(x => x.UserName == username);
        if (user == null) return false;

        // Hash their password.
        var hashedPassword = EncodePassword(password, user.PasswordSalt);

        if (!hashedPassword.Equals(password, StringComparison.InvariantCulture))
            return false;

        FormsAuthentication.SetAuthCookie(userName, true);
        return true;
    }
}

所以现在,我希望验证

  • EncodePassword被召唤。
  • FormsAuthentication.SetAuthCookie(..)被召唤。

现在,我并不关心这两者的含义。更重要的是,我不想测试这些方法。这必须在其他地方处理。我应该做的是验证那些方法是否被调用,如果可能的话......返回了预期的结果。

这是否正确理解'验证'对模拟的意义?

如果是这样,有人可以告诉我如何做到这一点。优先考虑moq,但我对任何事都感到满意。

3 个答案:

答案 0 :(得分:6)

您通常应该(IMO,至少)模拟依赖项,而不是正在测试的类中的其他方法。例如,您可以使用IPasswordEncoder接口用于EncodePassword调用。您可以模拟那个,并验证该方法是否已被调用...但在同一个类中它没有那么多意义。

我敢说许多模拟框架可以做这种嘲弄,但我个人不会鼓励它。如果您已经找到了希望能够注入不同功能的位置,请考虑将它们作为单独的依赖项。

答案 1 :(得分:4)

我认为,你有两个问题:

  1. EncodePassword属于FormsAuthenticationService,这使得模拟更难。 这个问题有两种可能的解决方案:

    1. 使EncodePassword受到虚拟保护,并创建Mock<FormsAuthenticationService>。在这种情况下,您应该能够Verify EncodePassword
    2. 将密码编码提取到其他服务中,因为它无论如何都没有与身份验证实现相关联。
  2. FormsAuthentication.SetAuthCookie(..)是一种静态方法。这里一个合理的解决方案是使用完全包裹FormsAuthentication的附加服务,但除此之外不做任何事情,因此您不必对其进行测试。

答案 2 :(得分:2)

在测试驱动开发中,您很少想要断言调用私有方法。验证EncodePassword被称为真正测试的是什么?如果我们详细查看该方法,我们会看到例如我可以更改方法以丢弃EncodePassword调用的结果而不会破坏测试。这当然不对 - 测试变得过于灵活,几乎没用。而是考虑这两种方式。

测试类

的公共接口

这是你通常想要的。您是否真的关心该方法是否被调用? Authenticate方法做了一些事情,我们想测试这个方法做了它应该做的事情。也许这样的断言来测试错误的密码不会进行身份验证:

Assert.That(service.Authenticate("Pure.Krome", "wrong pass"), Is.False);

这使您可以在不破坏测试的情况下更轻松地重构类。此外,测试的记录功能也会增加 - 这个类应该如何使用?

将方法中的功能提取到它自己的类

有时候,在测试期间不应该调用类的私有方法,而是嘲笑它。例如,静态方法调用FormsAuthentication.SetAuthCookie(userName, true)可能有副作用,或者可能在未在Web服务器上运行时抛出。在这种情况下,我的经验是最好注入一个描述所需功能的界面。在这种情况下,例如ICookieTarget使用SetAuthCookie方法,而不是可以模拟。