Realistic .NET integration tests with xunit and testcontainers
Spinning up real Postgres and Redis instead of mocking them
Unit tests leave much to be desired when writing tests for a server linked to a database. I wanted an easy way to test a server to improve maintainability and the velocity of shipping. After evaluation of available options, I chose to write tests using xunit.v3 and spinning up dependencies like Postgres and Redis using testcontainers.
Test runner; run the tests
The AssemblyFixture in xunit.v3 has been a life-saver for having singletons across the test runner. The following is the standard setup for a WebApiFixture modified to our requirements.
In this project, both Collections and TestCases are run in alphabetical order, sequentially. Parallel execution of tests is turned off since that model of running tests is not compatible with having a single database instance.
Abstracting away basic CRUD tests
With the generic reflection-based Faker, CRUD operations can be done easily without having to define any of the parameters. This also serves as a good test for how well the [Attributes] handle the inputs since the Faker can add some flakiness to the tests.
With how the CrudIntegrationTest abstract class is written, tests that inherit the class will always run a series of CRUD actions on the endpoint provided in the constructor. This makes authoring a basic set of tests very easy for endpoints that behave similarly, which was most of the code-base in this case.
Furthermore, having a running instance of the production server, database, and Faker helped me test locale support, new database versions, and SQL query stability across ORM upgrades.

Next steps
- While this worked well for a monolithic system, I'm not sure how well this approach works for a microservice architecture.
- Intuitively, I feel like there has to be a point where the testcontainers set-up becomes impractical/limited compared to a conventional Docker Compose based runner.