我正在玩一些单元测试和嘲笑。我正在尝试验证我的方法中的某些代码是否已被调用。我认为我不理解嘲弄权的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
,但我对任何事都感到满意。
答案 0 :(得分:6)
您通常应该(IMO,至少)模拟依赖项,而不是正在测试的类中的其他方法。例如,您可以使用IPasswordEncoder
接口用于EncodePassword
调用。您可以模拟那个,并验证该方法是否已被调用...但在同一个类中它没有那么多意义。
我敢说许多模拟框架可以做这种嘲弄,但我个人不会鼓励它。如果您已经找到了希望能够注入不同功能的位置,请考虑将它们作为单独的依赖项。
答案 1 :(得分:4)
我认为,你有两个问题:
EncodePassword
属于FormsAuthenticationService
,这使得模拟更难。
这个问题有两种可能的解决方案:
Mock<FormsAuthenticationService>
。在这种情况下,您应该能够Verify
EncodePassword
。 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
方法,而不是可以模拟。