Our Fitradar solution is using various third party services, and therefore the Integration tests in our solution became a very important part next to the Unit tests. In our backend application we are trying to follow the ideal Test Pyramid
but for some Use cases it makes more sense to run all the involved services rather than replace them with Mock objects, mostly because there is no complex logic involved in particular Use Case; it is rather straightforward without conditionals and loops. And on the other hand the Integration Tests give us more assurance that the Use Case is robust and works as expected. And so for some Use Cases we started to prefer Integration tests instead of Unit tests and our test pyramid became a little bit distorted. The following parts in our back end application are tested by Integration Tests:
- ASP.NET Core Model Binding. Once we discovered that produced JSON data for some C# objects is not exactly as we expected, and that some incoming JSON is not parsed as we expected we started to test and verify that incoming and outcoming data are transformed as we are expecting
- ASP.NET Core and third party libraries configurations defined in Startup file. The configurations can be tested only at runtime and therefore to test these configurations we have to run the test web server and make some requests against the server. Some of the configurations we are testing are following:
- Dependency injection
- AddMediatR and FluentValidation configuration
- ASP.NET Core Authentication and Authorization integration with Firebase Authentication
- Entity Framework produced SQL code. Especially for complex queries. And Entity Framework save and update logic for complex types where at runtime some nested objects might have different EntityState than parent object. And any complex logic interacting with Entity Framework
- Integration with Azure Services like Service Bus and Azure Web Functions
- Integration with Stripe
The crucial factor that made our team write more Integration Tests is the new ASP.NET Core test web host and test server client. It allowed us to quickly setup the test server with our back-end application and run the requests against our API. By using the test host we were able to test most of the configurations in the Setup file. It was harder to test our back-end parts that were part of the Azure Functions, but after some error and trial we were able to create a test web host for the Azure functions as well and issue the calls that triggered the Functions. And here is how we test the sport event cancellation Use Case that is triggered by message from Service Bus queue:
[Fact]
public async Task TestUnpaidEventCancelled()
{
var resolver = new RandomNameResolver();
IHost host = new HostBuilder()
.ConfigureWebJobs()
.ConfigureDefaultTestHost<CancelSportEventFunction>(webJobsBuilder => {
webJobsBuilder.AddServiceBus();
})
.ConfigureServices(services => {
services.AddSingleton<INameResolver>(resolver);
})
.Build();
using (host)
{
//Arrange
await host.StartAsync();
_testFixture.SeedSportEvent();
await _testFixture.BookSportEventInstance();
_testFixture.AddCommentToSportEventInstance();
//Act
await _testFixture.SendMessageAsync("CanceledEvents",
_testFixture.CanceledSportEventInstance.Id.ToString());
await _testFixture.WaitUntilSportEventInstanceIsCancelled();
//Assert
//_output.WriteLine($"Found {parsedOrders.Count} invoices.");
var calendarEvents = _testFixture.GetCalendarEvents();
Assert.Empty(calendarEvents);
var order = _testFixture.GetCancelledSportEventOrder();
Assert.NotNull(order);
var msg = _testFixture.GetVisitorsInboxMessage();
Assert.NotNull(msg);
Assert.Equal(MessageSource.CANCEL_EVENT, msg.Source);
var statistics = _testFixture.GetVisitorsInboxStatistics();
Assert.NotNull(statistics);
Assert.Equal(1, statistics.NumberOfCancelMessages);
}
}
P.S. Visit our website: https://www.fitradar.me/ and join the waiting list. We launch very soon!