T stands for testing. Understand it first. Chapter 3 — The right way.

Solution for the problems on unit testing and TDD.

Jakub Pruszyński
3 min readDec 29, 2021
Photo by Element5 Digital on Unsplash

After reading Chapter 1 and Chapter 2 of this series you are aware the tests are equally relevant as the production *. Furthermore, you can look at testing from a helicopter view of a software — an architecture. Now it is finally time to dig in into more technical details. Are you ready ? It is gonna be fun, so let’s start. At the beginning, we are going to get even with some myths, and then introduce the right way of unit testing.

*If you have not read, do not worry, it is not necessary, nevertheless I warmly recommend.

Myth: Each line of code should have a test

Implications:

  • Testing implementation details
  • Production cannot be refactored without changing tests
  • Not using objects or static methods even when useful
  • Some tests does not introduce any value

Truth: Each logical change in our code should end up with failed test

Myth: Unit test equals class test

Implications:

  • Tests are no longer a documentation of a functionality
  • Dimmed picture of business requirements towards functionality
  • Violation of TDD by not testing only public API (when class is internal)
  • Not testing real interactions between dependencies

Truth: Unit test is a test of functionality, that is expected behavior according to proper use case

Myth: All tests should be isolated

Implications:

  • Mocking everything
  • Logical duplication of some tests

Truth: Tests should be independent from each other and from IO

The ABC of testing

Testing rules:

  • Test functionality by C, and only by abstraction layer/contract (API)
  • If dependency of C is type A, then use real implementation
  • If dependency of C is type B, then use real implementation
  • If dependency of C is type C, then use mock/stub
  • For all dependencies of B being part of abstraction layer use mock/stub

In this approach you test each layer independetly and only through its contracts. The gaps between interactions among layers are fullfiled by integration tests. Applying all these rules lead to:

  • lower coupling between tests and production (the only mocks are APIs)
  • decreased number of tests — less code to maintain
  • tests are focused on functionalities instead of implementation details

Please, don’t forget to clap and follow, but only if I earned.

Cheers

PS. Dagger and Hilt are very useful to speed up unit testing, but it’s a story for another article. Soon.

--

--