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

发布于 2024-10-20 18:37:44 字数 1233 浏览 1 评论 0原文

我正在玩一些单元测试和嘲笑。我试图验证我的方法中的某些代码是否已被调用。我认为我不理解模拟的 Verify 部分,因为我只能验证 main 方法..这很愚蠢,因为这就是我 Act 的依据。

我正在尝试测试我的逻辑是否有效 - 所以我想我使用验证来查看该方法中的某些步骤是否已达到并已执行。

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

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 但我对任何事情都很满意。

I'm playing around with some unit tests and mocking. I'm trying to verify that some code, in my method, has been called. I don't think I understand the Verify part of mocking right, because I can only ever Verify main method .. which is silly because that is what I Act upon anyways.

I'm trying to test that my logic is working - so I thought I use Verify to see that certain steps in the method have been reached and enacted upon.

Lets use this example to highlight what I am doing wrong.

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

So now, I wish to verify that

  • EncodePassword was called.
  • FormsAuthentication.SetAuthCookie(..) was called.

Now, I don't care about the implimentations of both of those. And more importantly, I do not want to test those methods. That has to be handled elsewhere. What I though I should do is Verify that those methods were called and .. if possible ... an expected result was returned.

Is this the correct understanding of what 'Verify' means with mocking?

If so, can someone show me how I can do this. Preferable with moq but i'm happy with anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

若水般的淡然安静女子 2024-10-27 18:37:45

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

我敢说许多模拟框架可以进行这种模拟,但我个人不会鼓励这样做。如果您发现希望能够注入不同功能的地方,请考虑将它们设为单独的依赖项。

You should normally (IMO, at least) be mocking dependencies rather than other methods within the class that's under test. For example, you could have an IPasswordEncoder interface which is used for the EncodePassword call. You could mock that, and verify that the method had been called... but within the same class it doesn't make as much sense.

I dare say many mocking frameworks can do this sort of mocking, but I personally wouldn't encourage it. If you've found places you want to be able to inject different functionality, consider making them separate dependencies.

心不设防 2024-10-27 18:37:45

在我看来,您有两个问题:

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

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

As I see it, you have two problems:

  1. EncodePassword belongs to the FormsAuthenticationService, which makes it harder to mock.
    There are two possible solutions to this problem:

    1. Make EncodePassword protected virtual, and create Mock<FormsAuthenticationService>. In this case you should be able to Verify EncodePassword.
    2. Extract password encoding into a different service, since it is not coupled to authentication implementation anyway.
  2. FormsAuthentication.SetAuthCookie(..) is a static method. One reasonable solution here is to use an additional service that completely wraps FormsAuthentication, but does not do anything beyond that, so you will not have to test it.

拥抱我好吗 2024-10-27 18:37:45

在测试驱动开发中,您很少想断言调用了私有方法。验证 EncodePassword 被调用到底测试了什么?如果我们详细查看该方法,我们会发现,例如,我可以更改该方法以丢弃 EncodePassword 调用的结果,不会破坏测试。这当然是不对的——测试变得过于灵活并且没有什么用处。相反,请考虑这两种方法。

测试类的公共接口

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

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

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

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

有时,在测试期间不应调用类的私有方法,而应对其进行模拟。例如,静态方法调用 FormsAuthentication.SetAuthCookie(userName, true) 可能会产生副作用,或者在未在网络服务器等上运行时可能会抛出异常。在这种情况下,我的经验是最好注入一个描述所需功能的接口。在这种情况下,例如带有 SetAuthCookie 方法的 ICookieTarget 可以被模拟。

In test driven development you very rarely want to assert that a private method is called. What does verifying that the EncodePassword is called really test? If we look at the method in detail we see that for example I can change the method to throw away the result of the EncodePassword call without breaking the test. This is certainly not right - The test has become too flexible and is of little use. Instead consider those two ways around this.

Tests the public interface of the class

This is what you usually want. Do you really care wheter that that method is called or not? The Authenticate method does something, we want to tests that this method does what it should. Perhaps an assert like this to test that a wrong password does not authenticate:

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

This allows you to refactor the class more easily without breaking the tests. Also the documenting power of the test increases - How is this class supposed to be used?

Extract the functionality in the method into it's own class

Sometimes a private method of an class should not be called during the test, but rather mocked. For example the static method call FormsAuthentication.SetAuthCookie(userName, true) may have side-effects or may throw when not run on the webserver or such. In this case my experience it's best to inject an interface describing the needed functionality. In this case for example an ICookieTarget with a SetAuthCookie method than can be mocked.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文