使用 Moq 在 .NET 中实现可重用 Mock





0/5 (0投票)
我在测试项目中常用的实践 - 可重用 Mock
不久前,我写了一个关于在自动化测试中使用 Builder 模式的示例,我想接着分享另一个我在测试项目中常用的实践 - 可重用 Mock。
什么是 Mock?
对于那些刚接触自动化测试的朋友们来说,想象一下你有一个这样的类
public class Mailer
{
private readonly ISmtpClient _smtpClient;
public Mailer(ISmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
public async Task SendMailAsync(SendMailRequest request)
{
await _smtpClient.SendAsync(new SmtpMessage()
{
Port = 25,
To = request.To,
Credentials = CredentialsFactory.GetSmtpCredentials(),
// ...
});
}
}
注意:以上代码只是一个虚构的示例。这些类实际上并不存在。
如果我们想为这个类创建一个隔离的测试,而不需要实际发送任何电子邮件(即单元测试),那么我们需要一种创建 ISmtpClient
的假实例的方法,我们可以控制它,它不会实际发送邮件,但允许我们检查方法何时被调用,提供虚拟数据等。 这基本上就是一个 Mock。
使用 Moq 编写 Mock
如果我们要为上面的邮件发送类创建一个单元测试(使用 Mock),它可能看起来像这样
public class MailerTests
{
[Fact]
public async Task Can_send_mail()
{
// Arrange
var smtp = new Mock<ISmtpClient>();
smtp.Setup(m => m.SendAsync(It.IsAny<SmtpRequest>())).Verifiable();
// Act
var sut = new Mailer(smtp.Object);
await sut.SendMailAsync(new SendMailRequest()
{
//
});
// Assert
smtp.Verify(); // this will throw if SendAsync was not called
}
}
简而言之,我们创建了一个名为 smtp
的 ISmtpClient
的 Mock,设置它以 Mock SendAsync
方法,并将其标记为可验证 - 这样我们就可以稍后验证它是否被调用。我们将 Mock 传递给 Mailer
(被测服务),然后稍后验证该方法是否被调用。
听起来不错!有什么问题吗?
我不会说有什么问题 - 但它可以变得更可重用。假设我们有几个其他的测试(以及潜在的其他服务,它们也有自己的测试),它们都使用 ISmtpClient
。 我们每次都必须重复创建 Mock 的过程。 而相反,我们可以这样做
public class MockSmtpClient : IMock<SmtpClient>
{
public MockSmtpClient SetupSendAsync(bool veriable = false)
{
var setup = Setup(m => m.SendAsync(It.IsAny<SmtpRequest>()));
if (verifiable)
setup.Verifiable();
return this;
}
}
想法是现在我们有一个可重用的 Mock,可以在多个测试中重复使用。特别是当您 Mock 的服务包含许多将被使用的方法(例如存储库和其他与数据相关的服务)时,这可以使测试代码更加简洁和易读。 这也意味着,如果您想更改所有 Mock 的设置方式(例如:使它们全部可验证),您应该比尝试更改每个 Mock<T>
实例的地方少得多。
总之,就到此为止。 还有人遵循类似的实践,或者在自动化测试方面还有其他常用实践吗? 请在评论中告诉我!
再见!