TLDR;

Think about what type of test you are writing and why you are writing it. If it is too cumbersome and it adds little value then don’t write one. Fewer good tests > many bad ones.

A Little Background

I was taught TDD from the very beginning of my professional career which was a very good thing because to this day it’s a highly sought after skill. However…

A few years ago I decided that I didn’t like the way I was writing tests. They felt heavy and cumbersome and I felt that every time I changed some code I also had to change the reflected part of the code in the tests mostly because of mocks (but we will get to that). I then I asked myself. Why am I writing tests?

So I thought to myself I must not be writing them correctly. But I was… I was following everything I’d been taught, all the “best practices”. Everyone else appeared to like them but this is where the problem started if you said anything bad about writing tests you were either not writing them correctly or doing something else wrong. This happens sometimes in development. People become evangelists and fight for something they’ve learned like it’s life or death or some strange religion. “But but how will I get into developer heaven?”.

The idea of mocking something 🥁 that is called externally by a unit so that you can test the logic around it initially makes a lot of sense. This quickly becomes muddy however when you start relying on what is returned by that mock. Or it being called at all just to check that something is being morphed in a certain way. This holds little value. Basically a shittier version of snapshot tests.

Like this you can’t change code quickly. You have to essentially change it twice. Once in normal code and then again in a fugly reflection of the code.

An Example

@GetMapping("/sw/info/{name}")
public SWCharInfo info(@PathVariable("name") String name) {

    if (!validator.validateName(name)) {
      return "error"
    }

    RawSWInfo rawSWInfo = swInfoRepo.getRawInfo(name);

    return swInfoTransformer.transform(rawSWInfo);
}

Testing the above would be valid to most people in most places I’ve worked. Mocking the getRawInfo() obviously. There’s no need for this to become an integration test.

So if we aren’t testing it gets the data from the repository successfully then what are we testing? The validator. Ok that’s.. valid. And perhaps the transformer. Excellent, providing neither of those functions don’t reach out to any other services or repos (in this example they don’t), we’re good. But that sounds like two units are being tested at the same time instead of one.

Wouldn’t it be better and easier to test the validateName() and transform() functions separately. If they don’t require us to mock anything it means the tests will require very little maintenance if any, and they will serve as a pure set of tests.

I often see people talk about tests as documentation. “I always read the tests before reading the code”. In the great words of master Kenobi *“Then you are lost!”

The problem with this is most languages have been designed to be readable*. Adding a framework on top of this and trying to make it more readable than the code itself feels counter intuitive to me. It would be much better to write the unit’s code and the test’s code well and use the test as what it was originally designed for. To test the function works.

*fuck you COW lang.

In Conclusion

We should think more about what we write day to day. If tests feel like they don’t make sense or are weighing you or your team down then talk about it and have a think about why you are writing them in the first place. Most importantly don’t be afraid to ask “why?”.

Also

Episode 3 was terrible but I’ve spent far to long on /r/prequelmemes the last few days.