Use T4 Templates to Generate Integration Tests

In last week’s post we generated repositories to access our tables. But do they work, or did we overlook something? Let us find out if everything works as expected by generating integration tests for our repositories.

 

Limitations and the *.ttincludes files

Be aware of the limitations of the code generators we create in this blog series. The reason for that is the limited space in a blog post and not due to T4.

You can find the code for the _DbAccess.ttinclude and the _Imports.ttinclude in this post.

 

Challenges of integration tests

Our repositories offer us 4 methods to cover the CRUD operations and we now want a test for each method to make sure they work. As simple as this sound, there are a few challenges we need to address.

Getting meaningful data into our tests is the main problem. Especially when we consider that our generator could create tests for hundreds of classes. If we do not want to drown in an endless dictionary of test data, we need a different approach. We can cheat a little by creating a test data generator for each entity by hand and only call it from the generated integration tests. That way we get all the flexibility and still can generate useful tests.

The other important problem is that we need some data in the database for our Update(), FindById(), and Delete() tests. While we could use a pre-seeded database, we can cheat again and call the Insert() method in all our tests. That way we always have the right data in place and do not need to first add data to the database. Should our Insert() method have an error, all our tests will fail. This is a risk worth taking, then if everything fails, we know where to start checking for a problem.

The final obstacle we will run into is cleaning up after our tests. If we skip the housekeeping part, we will have 3 tests per repository that will leave data behind. But since we use a database in our tests, we can use transactions. If we do not commit the changes at the end, then no data will be left behind. We can keep the database in a clean state and still get all the advantages of writing to a database for our tests.

 

Create the integration tests generator

For our integration test code generator, we create a new template named IntegrationTestGeneratorFromDb.tt, add our *.ttincludes and write our test class inside a loop through our tables:

To reuse the code for reading the connection string, I created this small helper SettingsReader.cs inside the Helper folder:

 

Run the generator

We can run the custom tool on our generator to create the two integration tests in the FromDb\IntegrationTests folder:

Our two integration tests are inside the FromDb\IntegrationTests folder.

The tests for the CustomerRepository are inside the CustomerRepositoryTests.g.cs class:

The tests for the ProductRepository are inside the ProductRepositoryTests.g.cs class:

The code currently does not compile, then we are missing our two test data generators.

 

Create the test data generators by hand

We now need to add the test data generators in the TestDataGenerators folder and implement the two methods to return meaningful data.

Our CustomerGenerator.cs class can look like this:

While the ProductGenerator.cs class can have this implementation:

 

Run the tests

We can now compile our project and run our tests. They all should pass:

All our 8 tests passed.

 

Next

With our newly generated integration tests we know that our repositories work. We only covered the happy path of having valid data, but it only takes a small amount of extra work to test more edge cases.

Next week we explore our options when we need to combine generated with hand-written code.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.