I'm fairly new to unit testing in the .Net Core. I'm writing unit tests for an Interactor
class, whose responsibility is to getClient
s either by email or name using the IClientGateway
dependency. I've used XUnit
, Moq.AutoMock
and FluentAssertions
.
GetClientsInteractorTest.cs
[Trait("Clients", "GetClientsInteractor")]
[Collection(nameof(GetClientsInteractorTestsCollectionFixture))]
public class GetClientsInteractorTests
{
private readonly GetClientsInteractorTestsFixture fixture;
public GetClientsInteractorTests(GetClientsInteractorTestsFixture fixture) =>
this.fixture = fixture;
[Theory(DisplayName = "should return clients in clientsGateway")]
[MemberData(nameof(GetClientsInteractorTestsFixture.Params), MemberType = typeof(GetClientsInteractorTestsFixture))]
public async Task ShouldReturnClients(IEnumerable<Client> mockedClients, CancellationTokenSource cancellationTokenSource,
GetClientsRequest request, GetClientsResponse expectedResponse)
{
// Arrange
fixture.Mocker.GetMock<IClientGateway>()
.Setup(ClientGateway => ClientGateway.GetByNameAsync(request.Name, cancellationTokenSource.Token))
.ReturnsAsync(mockedClients.Where(client => client.Name.Equals(request.Name)))
.Verifiable();
fixture.Mocker.GetMock<IClientGateway>()
.Setup(ClientGateway => ClientGateway.GetByEmailAsync(request.Email, cancellationTokenSource.Token))
.ReturnsAsync(mockedClients.Where(client => client.Email.Equals(request.Email)))
.Verifiable();
// Act
GetClientsResponse actualResponse =
await fixture.Mocker.CreateInstance<GetClientsInteractor>()
.Handle(request, cancellationTokenSource.Token);
// Assert
if(request.Name != null)
fixture.Mocker.GetMock<IClientGateway>()
.Verify(mock => mock.GetByNameAsync(request.Name, cancellationTokenSource.Token), Times.Once(), "GetByNameAsync was not called");
else if (request.Email != null)
fixture.Mocker.GetMock<IClientGateway>()
.Verify(mock => mock.GetByEmailAsync(request.Email, cancellationTokenSource.Token), Times.Once(), "GetByEmailAsync was not called");
actualResponse.Should()
.Be(expectedResponse);
}
}
GetClientsInteractorTestsFixture.cs
public class GetClientsInteractorTestsFixture
{
public AutoMocker Mocker = new AutoMocker(MockBehavior.Strict);
public static IEnumerable<Client> mockedClients = new Client[] {
new Client()
{
ClientId = 1,
Name = "Victor",
Email = "someEmail@someProvider.com"
},
};
public static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public static IEnumerable<object[]> Params = new[]
{
new object[]
{
mockedClients,
cancellationTokenSource,
new GetClientsRequest(),
new GetClientsResponse()
},
new object[]
{
mockedClients,
cancellationTokenSource,
new GetClientsRequest(){ Name = "Victor" },
new GetClientsResponse(){ Clients = new List<ClientDto>()
{
new ClientDto()
{
Id = 1,
Name = "Victor",
Email = "someEmail@someProvider.com"
}
}
}
},
new object[]
{
mockedClients,
cancellationTokenSource,
new GetClientsRequest(){ Email = "someEmail@someProvider.com"},
new GetClientsResponse(){ Clients = new List<ClientDto>()
{
new ClientDto()
{
Id = 1,
Name = "Victor",
Email = "someEmail@someProvider.com"
}
}
}
},
};
}
The unit test itself (ShouldReturnClients
) its quite simple at first, but it requires 4 parameters (plus AutoMocker
) as a test fixture, found in GetClientsInteractorTestsFixture
. Is there a better way to make this test fixture by making it less repetitive? perhaps using something like AutoFixture
?
The short answer is - Yes, AutoFixture can help you reduce the boilerplate and keep your tests DRY. Below is what you tests could potentially look like with AutoFixture. For more details on using AutoFixture, you can visit the documentation, check out the Pluralsight course, read about AutoFixture on Mark Seeman's blog, or seek help on AutoFixture's GitHub Q&A section.
[Theory]
[MemberAutoDomainData(nameof(GetClientsInteractorTestsFixture.Params))]
public async Task ReturnsExpectedResponse(
GetClientsRequest request,
GetClientsResponse expectedResponse,
GetClientsInteractor sut)
{
var actual = await sut.Handle(request, default);
actual.Should().Be(expectedResponse);
}
[Theory]
[MemberAutoDomainData(nameof(GetClientsInteractorTestsFixture.ParamsWithName))]
public async Task RequestsClientsByNameWhenRequestContainsName(
[Frozen] Mock<IClientGateway> gateway,
GetClientsRequest request,
GetClientsInteractor sut)
{
await sut.Handle(request, default);
gateway.Verify(
x => x.GetByNameAsync(request.Name, It.IsAny<CancellationToken>()),
Times.Once(), "GetByNameAsync was not called");
}
[Theory]
[MemberAutoDomainData(nameof(GetClientsInteractorTestsFixture.Params))]
public async Task RequestsClientsByNameWhenRequestNameEmpty(
[Frozen] Mock<IClientGateway> gateway,
GetClientsRequest request,
GetClientsInteractor sut)
{
await sut.Handle(request, default);
gateway.Verify(
x => x.GetByEmailAsync(request.Email, It.IsAny<CancellationToken>()),
Times.Once(), "GetByEmailAsync was not called");
}