为单元测试中的服务添加其他方法

时间:2020-08-26 08:27:57

标签: c# unit-testing asp.net-core mocking

我们正在使用.NET Core 3.1。我们要测试是否将正确的消息作为参数传递给我们的电子邮件服务。可能的方法是模拟Send方法,然后将消息保存到内存队列/列表中。如何在不修改原始界面(IEmailService)的情况下做到这一点?在我们的模拟服务中,我们需要一个其他方法,该方法可以返回传递到其Send方法的所有消息,例如List<Message> GetSentEmails()

TestsBase.cs

public class TestsBase
{
    protected readonly IServiceScope _scope;
    protected readonly IPaymentService _paymentService;
    protected readonly IEmailService _emailService;
    
    public TestsBase(CustomWebApplicationFactory<Startup> factory)
    {
        _scope = factory.Services.CreateScope();
        _paymentService = _scope.ServiceProvider.GetService<IPaymentService>();
        _emailService = _scope.ServiceProvider.GetService<IEmailService>() as EmailServiceMock;
    }
}

[CollectionDefinition("MyCollection")]
public class MyCollection : ICollectionFixture<CustomWebApplicationFactory<Startup>>
{

}

PaymentServiceTest.cs

[Collection("MyCollection")]
public class PaymentServiceTest : TestsBase
{
    public PaymentServiceTest(CustomWebApplicationFactory<Startup> factory) : base(factory)
    {

    }
    
    [Fact]
    public void ConfirmPaymentTest()
    {
        // payment service also sends email
        _paymentService.Process(new Payment()
        {
            Amount = 203.12,
            Email = "test@test.com",
            ...
        });
        
        // we want to check if correct email was passed to email service
        var sentEmails = _emailService.GetSentEmails(); // HOW?
    }
}

CustomWebApplicationFactory.cs

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // mock email service
            services.AddScoped<IEmailService, EmailServiceMock>();
        });
    }
}

EmailService.cs

public class EmailService : IEmailService
{
    // implementation
}

public class EmailServiceMock : IEmailService
{
    private readonly List<Message> _sentEmails;

    public EmailServiceMock()
    {
        _sentEmails = new List<Message>();
    }
    
    // mocked implementation of all the methods
    // but we also need access to _sentEmails
    
    public void Send(Message message)
    {
        _sentEmails.Add(message);
    }
}

1 个答案:

答案 0 :(得分:1)

我建议您为此使用Moq库。您可以模拟IEmailService并使用Moq的Callback方法来捕获将哪些参数传递给Send方法。

您只需要调整服务的初始化。

[Fact]
public void ConfirmPaymentTest()
{
    var sentEmails = new List<Message>();
    var emailService = new Mock<IEmailService>();
    var paymentService = new PaymentService(emailService.Object);
    
    emailService
        .Setup(e => e.Send(It.IsAny<Message>()))
        .Callback<Message>(m => sentEmails.Add(m));   // instead of your GetSentEmails()
    
    paymentService.Process(new Payment()
    {
        Amount = 203.12,
        Email = "test@test.com",
        ...
    });
    
    // you can access your sentEmails list here
}

Moq是一个功能强大的库,用于测试.NET。还有更多功能可让您模拟方法的返回,验证调用方法的次数等。

相关问题