ASP.NET Core 2+ best practices and practical tools for testing, part 2, Use cases

Let’s practice!

We saw together in the previous article what were best practices. It was theory, but how can we apply that practices? I’ll share with you in this article my experience with a real life case I encountered in the past:

At this point nothing is shocking. We just have to unit test two methods that consume a DataTable to mapped in a list of Transaction objects or in a single Transaction object.

Now the bunch of tests that I’m going to show you could be shocking ! :), Are you ready?

So As you can see it’s a bit scary. These unit tests are going against most of the good practices described above, including:

  • Data setup is very complicated and takes time to be written
  • the name of each test method is not named properly, it’s very hard to understand what tests are doing, they have to be renamed
  • Two behaviors are tested in each methods, they have to be splitted in four test methods
  • Tests made on each service methods should be splitted into two nested classes for readability
  • Arrange, Act and Assert block are not well separated
  • Assertions are hard to read
  • Method parameters usage are not tested (we don’t know if parameters passed to the service method are well transmitted to the repository method)
  • The static method ToTransaction that is used for mapping in the service should be externalized in a public class to make the mapping testable, so the service has to be refactored (optional)

Let’s fix that!

Step 1

Based on good practices and after using tools described in the previous article, here is what our tests look like:

Note that AutoFixture is not compatible with DataTable, that’s why I created a temporary object named TransactionData, setup initially by AutoFixture and serialized into JSON with NewtonSoft and deserialized to a DataTable. That’s was the easiest way I found to simplify DataTable setup in the Arrange section.

Note that NSubstitute helped us (+ IRepository mocking) to check if parameters were well passed to the repository, example: _transactionRepositoryMock.Received(1).GetTransactionById(Arg.Is(_id));

Note also that ExpectedObject helped the test’s assertion to be simpler and more readable.

Finally you can see that FluentAssertion (when ExpectedObject is not used) really help to make assertions easier to read too. example: result.Should().BeNull();

Step 2 (optional)

Refactor the service to make the mapping from DataTable to Transaction object testable

Here we are ! The extension method named ToTransaction() is now testable.

Step 3 (optional)

Let’s test the ToTransaction extension method with the same practices:

Do you like that way to write tests? 🙂