有人可以澄清我对模拟验证概念的理解吗?
我正在玩一些单元测试和嘲笑。我试图验证我的方法中的某些代码是否已被调用。我认为我不理解模拟的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
通常(至少在我看来)您应该模拟依赖项,而不是正在测试的类中的其他方法。例如,您可以有一个用于
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 theEncodePassword
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.
在我看来,您有两个问题:
EncodePassword
属于FormsAuthenticationService
,这使得模拟变得更加困难。此问题有两种可能的解决方案:
Mock
。在这种情况下,您应该能够验证
EncodePassword
。FormsAuthentication.SetAuthCookie(..)
是静态方法。这里一个合理的解决方案是使用完全包装FormsAuthentication
的附加服务,但除此之外不做任何事情,因此您不必测试它。As I see it, you have two problems:
EncodePassword
belongs to theFormsAuthenticationService
, which makes it harder to mock.There are two possible solutions to this problem:
Mock<FormsAuthenticationService>
. In this case you should be able toVerify
EncodePassword
.FormsAuthentication.SetAuthCookie(..)
is a static method. One reasonable solution here is to use an additional service that completely wrapsFormsAuthentication
, but does not do anything beyond that, so you will not have to test it.在测试驱动开发中,您很少想断言调用了私有方法。验证
EncodePassword
被调用到底测试了什么?如果我们详细查看该方法,我们会发现,例如,我可以更改该方法以丢弃EncodePassword
调用的结果,不会破坏测试。这当然是不对的——测试变得过于灵活并且没有什么用处。相反,请考虑这两种方法。测试类的公共接口
这就是您通常想要的。您真的关心该方法是否被调用吗?
Authenticate
方法做了一些事情,我们想要测试这个方法是否做了它应该做的事情。也许像这样的断言可以测试错误的密码是否无法进行身份验证:这使您可以更轻松地重构类,而不会破坏测试。此外,测试的记录能力也会增加 - 这个类应该如何使用?
将方法中的功能提取到它自己的类中
有时,在测试期间不应调用类的私有方法,而应对其进行模拟。例如,静态方法调用
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 theEncodePassword
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: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 anICookieTarget
with aSetAuthCookie
method than can be mocked.