What is Unit Test roughly?
A piece of code that can quickly and independently verify another piece of code (a unit or subject under test)
The QUI of Unit Test
- Rule of 👍: under 100 milliseconds or 10 mins for the whole suite
- This is not a guide. As long as it’s fast enough for you to be run frequently
- Classic school: a class or group of classes that have no shared dependencies (aka collaborators) with another unit
→ Sociable Tests
- London school: a class
→ Solitary Tests
- Classic school: The Unit should be isolated from shared dependencies
- London school: The Unit should be isolated from all dependencies
What is not Unit Test?
- UI Tests: testing your system as if you’re a user.
This also includes API Tests and Command Line Interface if your system does not have a GUI
- Integration Tests: Testing multiple components of the system together.
These 2 types of test are as important as Unit Tests, however, are harder and slower to conduct. So Unit Test is still usually the most popular type of tests in a software project.
Why Unit Test?
Unit Test enables sustainable growth of softwares.
Softwares are like buildings in the manner that they both need to be built and maintained with a safety net to make sure when things go wrong there will be something to cover our assets.
Unit Test is the safety netting for softwares.
When maintaining softwares we need to refactor  our code on a daily basis and, more than unusual, accidentally break the expected behaviors. Because unit tests are written to verify those behaviors, they can immediately report the issues to us during development which prevents the bugs from affecting the users which, in turn, could save us a lot of money.
Unit Test leads to a Better Design
If you apply TDD, i.e. you write tests before your production code, you’re embracing modularity and clean code structure because all the components are loosely coupled, which means they could be changed without or with minimal impact to other components. TDD also drives out YAGNI which help you build a minimal system just enough to satisfy the requirements and so saving you cheddars 💸.
If you write tests after production code and find it really difficult or too complex to do your job then that’s a smell that your code is badly structured.
Though testability and design have a close relationship, good design is not the main goal of Unit Testing but a pleasant by-product. The main goal is still the ability to maintain and improve our systems confidently over time.
When to write Unit Tests?
Probably not for Prototype and Retirement phases
- Prototype: the goal of this phase is a working MVP, not growth or maintenance. Writing Unit Tests will slow you down and they don’t add many benefits anyway because the requirements can change drastically.
- Retirement: Writing new tests in this phase won’t bring benefit because the system has matured enough and will soon be retired. Existing tests should still be taken care of, however.
How to write Unit Tests?
What to Test?
- All public methods (aka public interface)
- Private/protected methods are internal implementation and should be tested via the public ones.
- If you really think your private methods worth their own testing then it’s a smell that your class is doing too many things which violates the Single Responsibility Principle of SOLID. You should break down your classes, extract those private methods to their own classes to test them individually.
- Internal methods can be made visible to unit tests in frameworks like .NET
- Don’t test trivial code, e.g. code that doesn’t do any logic but just forward results of another code.
- Also, to answer the question if your logic is worth testing it depends on your deadline. Any logic is worth testing in theory if you have time. Problem is we usually have to deliver under tight deadlines and therefore hardly have time to test everything. Discuss with your team or Product Owner before investing time for a feature.
- AAA: Arrange, Act, Assert
- BDD: Given, When, Then
- One test class per unit e.g. Calculator → CalculatorTest
Test Mocks and Stubs
Mocks and Stubs are 2 types of Test Doubles. There are 5 types of them in total but mocks and stubs are the most popular. Though some people use them interchangeably and some consider everything is mock, understanding their differences is important to write good unit tests.
- Stubs emulate incoming data
- Mocks emulate and examine outcoming interactions
- Because test code is as important as production code, it deserves the same level of care as production code. We should keep them clean, well-structured and easy to read.
- And because readability matters, don’t try to over DRY your code.
A little duplication is fine if it can improve readability.
Try to balance between DRY and DAMP
When in doubt use Rule of Three
- Aim for testing one behavior per test so the tests are clear and focused. When they fail, the reason is obvious.
Having multiple assertions per test is fine.
- Tests should cover happy paths and edge cases but should not be too tied to production code.
Tests tightly coupled to production code can get really annoying because they will break every time you refactor your code.
Tests should only verify the expected outcome, not internal implementation.
In this article I’ve listed out things that took me years to really understand. Things that seems to be subtle like the differences between stubs and mocks turns out to be indispensable to write solid tests. Hopefully this could help you start learning Unit Test a bit easier.
Having said that, I think it will still take you time to really understand the Art of Unit Testing, given that you’re a beginner. Also, because I’m just stratching the surface here, you will need to practice writing a lot of tests in your projects and read a lot more articles. And if you do, I strongly recommend the resources in the References section below.
- Refactoring is changing the internal structure of your code without changing its external behavior
- Unit Test by Martin Fowler
- Mocks Aren’t Stubs by Martin Fowler
- The Practical Test Pyramid Ham Vocke
- Opinion: When should I write unit tests? by Jeff Lombard
- Unit Testing Principles, Practices, and Patterns by Vladimir Khorikov
- Exploring Mocks in Unit Testing by Vladimir Khorikov (Free Book)
- The Art of Unit Testing by Roy Osherove
- Working Effectively with Legacy Code by Michael Feathers