avatarlazlojuly

Summary

The website content provides guidance on how to approach unit testing effectively, emphasizing the importance of treating it as an integral part of the development process and not just an afterthought.

Abstract

The article "How to get started with Unit Testing? Part #1" addresses the common misconception of unit testing as a mere chore by advocating for its integration into the development workflow. It suggests that unit testing should be held to the same standards as production code, promoting the use of Test-Driven Development (TDD) as a beneficial approach rather than a strict framework. The author encourages writing tests in plain English to clarify the desired outcomes of a unit, fostering cleaner and more organized codebases. The article also discusses the concept of trusting code through testing, the flexibility in defining units, and the importance of testing behavior over implementation while acknowledging the necessity of mocking and spying in collaborative units. It concludes with the advice to cover all logical paths in testing and to break down complex units for better testability, summarizing the key points for effective unit testing.

Opinions

  • The author believes that unit testing should be an essential part of the development process, not a separate task.
  • TDD is recommended as a flexible approach to improve code quality, rather than a rigid methodology.
  • Tests need not be perfect; they should be used iteratively to provide valuable feedback during development.
  • There is no strict rule for identifying a unit; it is up to the developer's pragmatism and the goal of maintaining modularity.
  • The Unix philosophy of "doing one thing and doing it well" is echoed as a principle that supports unit testing and modular design.
  • The author suggests that while testing behavior is generally preferred, it is sometimes necessary to test implementation details, especially when units interact.
  • The use of stubbing and spying

How to get started with Unit Testing? Part #1

It’s done! Only adding tests now.

I guess many of us can relate to a situation depicted above. A place where, unit testing is considered as a chore.

Something, that needs to be done as an addition to implementation as opposed to being vital part of the development process.

I think this mainly happens because unit testing demands a discipline to hold test code to the same standard as the production code.

Am I saying we should do TDD religiously?

Oh, no. No way!

TDD should be taken as an approach rather than as a rigid framework.

Try to write a small test first, even if it is just in plain English. TDD forces us to think about what we really want from the unit; inputs, outputs, interface etc. resulting in much cleaner and organised codebase.

Also, remember tests do not need to to be perfect, neither does production code. Use TDD iteratively to get quick valuable feedback during development.

In code we trust!

A software application consists of many building blocks (we can call them units). We write the tests to prove these units work in isolation.

Once we made sure the they work, we can trust them!

In code we trust!

How to identify a unit?

This is an easy one. It’s entirely up to us! There are no hard and fast rules for this.

We can consider a bunch of functions as one unit or test a single function as a separate unit. Can take a class and call it a unit or write tests for framework provided abstractions (Angular or React components).

Test a group of functions as one unit vs. single function as separate unit

So there is no rule, but there is pragmatism.

We can use a very simple rule:

If a unit proves difficult to test, then it’s very likely that it needs to be broken down into smaller components.

Unit composition FTW!

Unit testing encourages modularity.

It encourages practices that are even noted in the popular (and now at least 40 years old) Unix philosophy:

“Write programs that do one thing and do it well.”

“Write programs to work together.”

Test behaviour vs. testing implementation

Another interesting approach I’ve learned is:

“Test behaviour not implementation”

This a great advice but can be confusing. It’s great for units that do not use dependencies or for end-to-end tests where testing implementation is a no no.

Testing behaviour vs. implementation

However, in many cases we can’t just blackbox our units.

This is because units interact.

Collaborator units require mocks and spies during testing

Software components don’t usually exist in isolation. In a real application many units are created to collaborate with each other.

So when we test a unit that was designed to use another unit as a dependency, we need make sure that the dependent unit is being used correctly.

This, often results in various stubbing and spying practices which are definitely the weakest and most debated parts of unit testing.

Travel the paths!

When the component under test has any kind of branching (if else, switch statements and so on), it is important to cover all the logical outcomes.

Travel the paths!

Okaaay, but what if the unit has way too many variable results?

Well, that means that the component could and should be broken down into smaller units until it’s a fully testable.

Summary

  • Prove your code works so you and others can trust it.
  • Hold test code to the same standard as the production code.
  • Test all the paths (including the unhappy / erroneous paths)
  • Hard to test? Break it down!

Part #2 — Let’s code!

Part 2 — coming soon!

Thank you for reading the first part of my “How to get started with Unit Testing?” series. I aim to publish the second part (covering realistic code examples!) in just a couple of weeks time.

Follow me here on Medium or on Twitter to get notified as soon as the followup parts are online.

Useful resources on testing

Testing
Unit Testing
Programming
Software Testing
Recommended from ReadMedium