Treat Your Tests Like Your Code

Written by alexaitken | Published 2018/07/03
Tech Story Tags: testing | software-engineering | software-development | clean-code | best-practices

TLDRvia the TL;DR App

Over my many years as a developer, I have noticed a pattern. This pattern is to do with unit tests. Especially those who write unit tests. What I’ve noticed is that a lot of developers don’t treat their unit tests like they treat their (production) code. That is, they under-engineer unit tests and end up with unreadable tests that are impossible to debug, and no one understands what the tests are testing.

Using Constructors

If you’re using DI (Dependency Injection) in your production code, your test code usually requires a lot of set up. We’ve all seen this mess. new x(); new y(); new z(); Eventually, you end up with setup that takes half a page. But, luckily for you, there are patterns to deal with this. And I like the rule of using a constructor in as few places as necessary. What that means, is that you should dig into your patterns book and start by maybe using the Builder pattern.

public class RepositoryBuilder{public RepositoryBuilder(){DbConnection = new MockDbConnection();}

public IDbConnection DbConnection { get; set; }

public Repository Build(){return new Repository(DbConnection);}}

You don’t have to follow the above, but basically, this will abstract away the constructor and set some defaults for you. You can set the DbConnection yourself if you need to test something specific with the behaviour between the DbConnection and the Repository. Now your tests will look like below, and you can even go further than that.

[TestFixture]public class RepositoryTests{[Test]public void CanGetDataFromRepository(){var repository = new RepositoryBuilder().Build();var expectedData = CreateExpectedData();

var data = repository.GetData();

data.ShouldBe(expectedData);}}

You can see that the code is readable. There’s no more copy+paste of constructors, which means that when you refactor — it’ll be so easy because you’ll only have to change one place. Now, I’m not saying that this rule is 100% correct, but when you can decouple your tests a little bit from the implementation, it’ll go a long way to making your life easier.

Keep It Simple Stupid

Don’t try to test multiple things in one unit test. If your method is doing more than one thing then maybe it’s time to consider refactoring that method. But don’t worry! There’s help on the way. You can write multiple unit tests that test different things in your method. Maybe you have a decorator, and you want to test that what you add works and that you’re still calling into the class that you’re decorating. That’s okay! Just do it in two unit tests.

Mocking

I like mocking a class’s behaviour. I prefer it to creating a Stub class. There are tonnes of mocking frameworks out there. There’ll probably be a mocking framework for your choice of language. For C#, I like to use Moq — but if someone could recommend a better mock framework — I’d be up for it. For JS — you can see from my earlier posts that I like Jest. It does everything we need.

Fluent Syntax/Method Chaining

I like the Fluent Syntax for assertions and builders. It helps to create a story that whoever reads the test can follow and understand without having to look into the code. But you can go too overboard — which is what has happened sometimes. I’ve seen people go crazy with the Given/When/Then phenomenon. An example:

[Test]public void DocusignTest(){Given.ADocuSignService.ThatReturnsInvalidTempResult().GetDocuSignURL(Given.AHotelId).ShouldNotBeNullOrEmpty();}

YES: It’s readable

  • YES: It’s understandable.
  • YES: It’s short.

However, the minus points are:

  • NO: What’s it testing?
  • NO: Where do I debug?
  • NO: I can’t glance at this test and understand the inner workings. I’ll have to go through every method to find out what each does.

I much prefer the syntax of “Arrange, Act, Assert”. You do you set up (arrange). You call the method in question (act). You determine the truth (assert).

[Test]public void DocusignTest(){// arrangevar service = Given.ADocuSignService.ThatReturnsInvalidTempResult();

// actvar result = service.GetDocuSignURL(Given.AHotelId);

// assertresult.ShouldNotBeNullOrEmpty();}

I find that much more understandable than the first example. Maybe you don’t? Let me know how you structure your tests.

I hope that you’ve learnt a little bit from this brief introduction to improving your tests. To summarise, use builders (or something similar) to decouple your tests from changes to the object you’re testing. It’ll make your refactoring that much easier. Keep your tests simple. Unit test one thing at a time. Try mocking! It’ll save you creating Stubs. And finally, try the fluent syntax — just don’t go too overboard.

Originally published at www.alexaitken.nz on June 18, 2018.


Published by HackerNoon on 2018/07/03