In my unit/component tests, I often assert that all the correct values are passed into Cosmos Container from the api request payload. By asserting that all the parameters are correctly passed, I can make sure the endpoint is doing its job correctly.
private readonly TestApplicationFactory<Startup> _factory;
private readonly ITestOutputHelper _output;
private readonly HttpClient _client;
private readonly Fixture _fixture;
public CreateTransactionTests(TestApplicationFactory<Startup> factory,
ITestOutputHelper output)
{
_factory = factory;
_output = output;
_client = factory.CreateClient();
_fixture = new Fixture();
}
[Fact]
public async Task Should_store_correct_transaction_details_in_the_database_and_return_200_Ok()
{
// Arrange
_factory.MockContainer.Reset();
var transactionId = Guid.NewGuid();
var request = _fixture.Build<CreateTransactionRequest>()
.With(x => x.Currency, "EUR")
.Create();
TransactionData transactionData = null;
var mockItemResponse = new Mock<ItemResponse<TransactionData>>();
mockItemResponse.Setup(x => x.StatusCode)
.Returns(HttpStatusCode.OK);
_factory.MockContainer.Setup(x => x.CreateItemAsync(It.IsAny<TransactionData>(),
It.IsAny<PartitionKey>(),
It.IsAny<ItemRequestOptions>(),
default(CancellationToken)))
.ReturnsAsync(mockItemResponse.Object)
.Callback<TransactionData, PartitionKey?, RequestOptions, CancellationToken>(
(t, p, r, c) => transactionData = t);
// Act
var response = await _client.PutAsync($"/v1/transactions/{transactionId}", request.ToStringContent());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(request.AccountId, transactionData.AccountId);
Assert.Equal(request.Amount, transactionData.Amount);
Assert.Equal(request.Currency, transactionData.Currency);
...
}
xUnit's IClassFixture
IClassFixture lets you use a shared class to use in your tests. I put all of my my infrastructure plumbing code there.
public class TestApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
public Mock<Container> MockContainer;
public TestApplicationFactory() { }
protected override IWebHostBuilder CreateWebHostBuilder() => new WebHostBuilder();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
MockContainer = new Mock<Container>();
builder
.UseKestrel()
.UseEnvironment(Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development")
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((context, configBuilder) =>
{
configBuilder.SetBasePath(context.HostingEnvironment.ContentRootPath);
configBuilder.AddJsonFile("appsettings.json", true);
configBuilder.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true);
})
.UseStartup<Startup>();
builder.ConfigureTestServices(services =>
{
services.AddSingleton<ITransactionRepository>(x =>
new TransactionRepository(MockContainer.Object, new NullLogger<TransactionRepository>()));
});
}
}
I have public MockContainer that mocks Cosmos Container.
AutoFixture
AutoFixture is very handy to populate a request object.
var request = _fixture.Build<CreateTransactionRequest>()
.With(x => x.Currency, "EUR")
.Create();
Set up Mock Cosmos Container
So that I can verify what parameters the Container receives. I use moq's Callback. I prefer it over Verify becasue I can assert each value per each line.
var mockItemResponse = new Mock<ItemResponse<TransactionData>>();
mockItemResponse.Setup(x => x.StatusCode)
.Returns(HttpStatusCode.OK);
_factory.MockContainer.Setup(x => x.CreateItemAsync(It.IsAny<TransactionData>(),
It.IsAny<PartitionKey>(),
It.IsAny<ItemRequestOptions>(),
default(CancellationToken)))
.ReturnsAsync(mockItemResponse.Object)
.Callback<TransactionData, PartitionKey?, RequestOptions, CancellationToken>(
(t, p, r, c) => transactionData = t);
One thing to note is I used Mock<ItemResponse<TransactionData>>(). It's becasue ItemResponse has Internal constructor and I canot new up the class. So I used mock instead.
Assert
var response = await _client.PutAsync($"/v1/transactions/{transactionId}", request.ToStringContent());
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(request.AccountId, transactionData.AccountId);
Assert.Equal(request.Amount, transactionData.Amount);
Assert.Equal(request.Currency, transactionData.Currency);
...
Top comments (2)
Thanks, saved me a few days of work :)
Hello Andrew,
Have you been able to mock the ReadItemAsync also? Would be willing to share?
Kind Regards,
Mário