Reputation: 2269
We are using .NET Core 3.1
. We want to test whether correct messages are passed as an argument to our email service. Probably the way to go is to mock Send
method and just save the message to in-memory queue/list. How can we do this without modifying the original interface (IEmailService
)? We need one additional method in our mocked service which returns all the messages that were passed to its Send
method, something like 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 = "[email protected]",
...
});
// 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);
}
}
Upvotes: 2
Views: 452
Reputation: 1559
I'd suggest you use Moq library for this. You could mock your IEmailService
and use Moq's Callback
method to capture what arguments were passed to the Send
method.
You would just need to tweak the initialization of your services.
[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 = "[email protected]",
...
});
// you can access your sentEmails list here
}
Moq is a really powerful library for testing .NET. There are more features that allow you to mock the return of a method, verify the count of how many times a method was invoked, etc.
Upvotes: 2