, 39 tweets, 9 min read
My Authors
Read all threads
Unit tests are key to producing more robust and less buggy software.

In this thread I will discuss everything about unit tests. Examples will be in Swift and XCTest framework, but the principles apply generally to all languages and test frameworks.

Thread 🧵👇
1. What is a unit test?

Unit test is a method that runs a piece of code to verify that a known, fixed input produces a known, fixed output. If the assumptions turn out to be wrong, the unit test considered failed.
2. Known input and fixed output are very important. Tests that do not possess these two qualities are flaky. Flaky tests are non-repeatable. It is hard to track down the error if they fail. Such tests can be worse than useless.
3. Why to write unit tests?

We all make mistakes. Unit tests verify that our code works and it keeps working. They allow us to make sweeping changes all over the code base.
4. Economics of Test Automation shows that unit tests pay off quickly xunitpatterns.com/Goals%20of%20T…
5. What to test?

When you test something, you refer to it as the system under test (SUT).

System under test is a sum of actions that has a single noticeable end result in the system. Typically, SUT is a single method or function.
6. Single and noticeable are very important.

Single means that a test must verify one concert. A good way to think about it: if the first check fails, does it matter what happens to the next one? If yes, you probably test more than a “unit”.
7. Noticeable means that we should not look at the privates to “feel” the end result. The test must invoke only public APIs and behavior.
8. What makes a good unit test?

It's not enough to define what a unit test is. More importantly, we must highlight what makes a good unit test.

There is not point in writing a bad unit test, unless you want to learn how to write a good one.
9. A good unit test must have three qualities:
- Readable
- Maintainable
- Trustworthy

The rest follows from these three.
10. Readability

Every time something breaks in our code, we examine failing tests. This happens very often. We must understand what's wrong just by looking at test code. We are likely to delete or rewrite the test if we can't figure things out.
11. ... Readability

Readability can be broken into:
- Naming unit tests
- Naming variables
- Maintaining uniform test structure
- Writing descriptive assert messages

I recommend to fix these things in a style guide. Like this one google.github.io/swift/
12. Test structure

There is a well-established practice to structure a unit test around three steps:

- Given: prepare test data and system under test.
- When: poke system under test.
- Then: verify that output is as expected.
13. Use specialized assertions

Wherever possible, use the most specialized assertion. This improves test reports and makes code more readable.
14. Maintainable

Maintainability is another core quality of a good unit test.

Otherwise tests can ruin project deliveries and are candidates to be disabled when schedule becomes aggressive.
15. ... Maintainable

Ideally, we should change our tests only when our system's requirements change. Refactoring should not not result in a cascade of changes in the tests.
16. How to make tests maintainable?

- Verify one thing per test.
- Test only publics and internals. Do not weaken encapsulation just to verify private behavior.
- Do not put code into production which is there only to support testing.
17. ... How to make tests maintainable?

- Reuse code. If tests contain lots of duplication, it’s time to introduce new abstractions. Create helper methods, introduce new test classes. Extract commonly used test data.
17. ... How to make tests maintainable?

- Make tests atomic. Tests should not depend on the order in which they run.
- Every test must start fresh. Cleanup before and after each test if they modify globals.
18. ... How to make tests maintainable?

- Isolate system under test from its dependencies. Use mocks to mimic their behavior for testing.
- Do not call a test method from another test method. This couples the tests and makes them fragile.
19. ... How to make tests maintainable?

It's common to initialize SUT and dependencies in setup(). Don't do that!
- setup() couples tests and makes them fragile.
- It grows over time, making tests unmanageable.
- Tests get more dependencies than they actually need.
20. ... How to make tests maintainable?

Don't test too much:
- Avoid testing internal state even if it is public. It can change later.
- Don't assert three values when one is enough.
21. ... How to make tests maintainable?

- Never use real database, network, device properties and settings (time, language, locale) in tests. They are the primary source of fuzziness.
22. How to make tests maintainable?

Whenever possible, avoid testing asynchronous code:
- Mock all dependencies, so that they become synchronous.
- Break an async test into two. One test can verify SUT before the async execution and one after.
23. How to make tests maintainable?

If it's impossible to avoid testing async code, use special patterns to control test flow:
- Signaling. Notify the waiting test that an async operation has completed.
- Busy waiting. Re-assert a value in a loop until the condition holds.
24. Trustworthy

Trustworthiness is the last core quality of a good unit test.

A test is trustworthy when it makes clear what’s going on and that you can do something about it.
25. What is trustworthiness?

When a test passes, you trust such test and that the code works under this scenario. And when the test fails, you don’t tell yourself that this does not mean that the code is not working.
26. What makes tests trustworthy?

Tests must always have 100% pass rate.

Having at least one failing test results in a broken window effect.

Having tests that sometimes fail will soon infect the whole suite. It is usually a sign of more serious problems.
27. What makes tests trustworthy?

Avoid test logic. No ‘for’ or ‘while’ loops,. No ‘if’, ‘else’ or ‘switch’ statements. Not even a ‘do-catch’. They are another source of flaky and buggy tests.
28. What makes tests trustworthy?

Unit tests must be fast. How fast? I believe that less than 0.1 second per test. We have around 5000 tests in our project. It would take > 8 minutes to run them. This is too long of a feedback when doing TDD.
29. Designing code for testability

A piece of code is testable if we can quickly and easily write unit tests against it.

Testable code must have two properties:
- SUT can be isolated from its dependencies;
- SUT must have points where we can “sense” the change.
Unit test are intended to work closely to the code they check. Imagine testing the whole program through it ‘main’ method. This sounds ridiculous. That’s why we must be able to isolate SUT from the rest of the code.
31. Dependency injection is the technique that allows us to isolate a piece of code from its dependencies.

We strive to:
- have public dependencies,
- depend on abstractions, instead of implementations.

Such dependencies are easy to mock during testing.
32. “Sensing point” refers to a piece of code that tells us that something has changed in the system. Typically, it is a change of a property or firing a notification.
33. Recommended resources on unit testing

Books 📖:
- xUnit Test Patterns amazon.com/xUnit-Test-Pat…. Classical book on unit testing, highly recommended.
- The Art of Unit Testing amazon.com/Art-Unit-Testi…. Teaches writing tests that are readable, maintainable and trustworthy.
34. ... Books 📖

- Working Effectively with Legacy Code amazon.com/Working-Effect…. How to deal with a large legacy of untested code. Explains high-level patterns that can dramatically improve your maintenance tasks.
35. Talks 📹

- The Clean Code Talks . Must see introduction into unit testing.
- Effective Unit Testing .
- Testing legacy code .
36. Articles 📃

Martin Fowler on unit testing:
- Basics martinfowler.com/bliki/UnitTest…
- Test pyramid martinfowler.com/articles/pract…

Microsoft on unit testing:
- Basics docs.microsoft.com/en-us/visualst…
- Best practices docs.microsoft.com/en-us/dotnet/c…
37. My articles on unit testing 📃:

- Getting started vadimbulavin.com/real-world-uni…
- Async testing vadimbulavin.com/unit-testing-a…
- Busy assertion pattern vadimbulavin.com/swift-asynchro…
- Best practices vadimbulavin.com/unit-testing-b…
- Unit testing the UI vadimbulavin.com/unit-testing-v…
Missing some Tweet in this thread? You can try to force a refresh.

Enjoying this thread?

Keep Current with Vadim Bulavin

Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Follow Us on Twitter!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3.00/month or $30.00/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!