DEV Community

Rex
Rex

Posted on • Updated on

Understanding Unit Test From The Best Book On The Topic

Learning to write good and effective unit tests is very hard. Because of it, I used to tell myself that if I follow best practices, adopt good software architecture and patterns, break code into manageable and straightforward code pieces, I don't need tests and could produce the same quality software. It's a lie. Writing good tests is the only thing that can lead us to best practices, to suitable architectures and patterns.

I now religiously believe that it is a good test suite that defines the quality of any software. What is a good test? To answer this question, we must break the question into two questions: What is a unit test? What makes a test a good test?

There are many answers out there. The best answers are below, from the book "UNIT TESTING: PRINCIPLES, PRACTICES, AND PATTERNS" by Vladimir Khorikov.

What is a unit test?

 A unit test is a test that

  • Verifies a single unit of behaviour,
  • Does it quickly,
  • And in isolation from other tests.

What makes a test good?

 A good unit test has the following four attributes

  • Protection against regressions
  • Resistance to refactoring
  • Fast feedback
  • Maintainability

There we have it. A unit test verifies a single unit of behaviour, not a single piece of code, not a single class, not a single function etc., but a single unit of behaviour.

To do this, we must be mindful of the following:

  1. Try to get as close to an actual business use case as possible and as far as possible from implementation details. Mimic user actions and assert user expectations. Never assert what the code should do internally. It is worth to mention that for JavaScript, the library @testing-library embraces and enforces this principle.

  2. Isolate business logic to pure functions/methods (without side effects, given the same input, they always return the same output). Use pure functions anywhere when possible because Input/Output based tests are the best tests.

  3. If one behaviour is too complicated to test, break it up. Ok, I tried my best, but it is still not easy to test? There is more room for improvement. Keep trying until they are easily testable.

  4. Only mock external unmanaged dependencies if possible. If not, do not assert against managed dependencies

  5. If it's not a behaviour, don't test it.

Beware, bad tests are worse than no tests! 

If tests verify a single piece of code and assert what they do internally, they are pinning down the implementations. It is the worst thing that could happen to a project. They don't help deliver what the software is supposed to do for its users. They make the code base impossible to refactor hance hinder the team's ability to improve the software and add/change features. It is a waste of time to write them. It is worse than no tests at all.

Discussion (2)

Collapse
joelbonetr profile image
JoelBonetR

Hi Rex, nice post. I only disagree in a little phrase over that:

"Writing good tests is the only thing that can lead us to best practices, to suitable architectures and patterns."

I believe that are knowledge and experience which leads to best practices.

Architectures and patterns, on the other side, are meant to solve specific use cases so it's the ability to analyse the problem we're about to solve which lead us to the adequate architecture and/or pattern for this specific case.

I'll define tests as the key point that permit us to maintain and refactor our software without the deep fear of breaking other use-cases that we are not aware of (and thus, will not be manually tested).

A join statement for both things would be: "don't try to refactor and develop at the same time" because if you develop, you'll need to update the tests as well, in which case you'll never know if the reason for the tests being in red is either the refactor or the development (or both) or an even worse situation, the tests being in green just because the update you wrote on them 😆

This is for businesses people: remember that it's cheaper to have tests in the software than getting an entire QA team, for developers it's also quicker and easier to solve an issue prompted by the tests than dealing with other people and get stuck in emails and calls.

Best regards

Collapse
rexebin profile image
Rex Author • Edited on

Thank you for your comment! You are absolutely right about the importance of testing and that we should not refactor/abstract too early. With testing, refactoring come naturally with comfort after green tests.

If done right, the process of writing testing is problem solving, which definitely leads to suitable architecture and pattern that make the testing easier and code cleaner.

I think knowledge and experience don’t necessarily lead to best practices. Knowledge and experience guided by testing however guarantees the best practices and is also a fast lane to writing good software.