TDD Unit Tests
Tags: qa, tdd, agile
Published: 2022-08-08 07:19:08

---
title: TDD ≠ Unit Tests
published: true
date: 2022-02-07 00:00:00 UTC
tags: qa,tdd,agile
canonical_url: https://jhall.io/archive/2022/02/07/tdd-unit-tests/
---

When I mentioned [my recent TDD failure](https://jhall.io/archive/2022/02/05/the-importance-of-seeing-red/) on [LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:6894757550479216640/), it sparked a conversation about TDD vs Unit Tests, in which I made the claim:

> The unit of test in TDD is a behavior. That may correspond to a “unit.” It often does not.

Someone in the thread asked me for some examples of what I mean, and here I wnat to provide that.

I imagine most of us are at least conceptually familiar with the [test pyramid](https://en.wikipedia.org/wiki/Test_automation).

 ![](https://jhall.io/images/test-pyramid_hu351d7094edd85027c03ef5756aacd2d2_245256_0x250_resize_q85_box_3.png)

There are many variations, each with its own specific layers, but it’s the concept that matters here.

My point was that when doing TDD, you may write tests that fall _anywhere_ on that pyramid. This often does _not_ correspond to any standard definition of a [unit test](https://en.wikipedia.org/wiki/Unit_testing).

So let’s construct an example.

Imagine we have a web app with a broken login page. If you enter a blank password, the system simply crashes, rather than returning an error.

- **E2E-based TDD** The system was built without automated tests. You don’t even have the ability to mock the authenticatoin database. Getting it under test properly is a big task, but you can’t take the time to get there before doing your Red/Green/Refactor stage to fix this bug. So you write an automated E2E test using something like Selenium. You then do your debugging, solve the problem, and now the test passes.
- **Integration-based TDD** Let’s say the system is in a little bit better state, but still not easily testible. In this example, you can mock the authentication database in your tests. You still need to launch an instance of the service, connected to your mock database, but at least it’s not the _whole_ system. You write your test. It fails. You do your debugging, fix the bug, and now your test passes.
- **Unit-based TDD** In the ideal scenario, you actually have a system that’s been built (or refactored) with testing in mind from the ground up. In this case, you can easily isolate the auth module, and write a simple behavioral test around just this aspect of the system. It fails. You fix the bug, and now the test passes.
- **Sub-unit-based TDD** This one is controviersial in some circles. There’s a school of thought that you should only ever test public methods, and while this is good general advice, I believe its appropriateness depends a lot on which language/framework/stack you’re using, and how much flexibility it affords you. There are times when I write tests around private functions or methods. Although admitedly, I can’t imagine this would be appropriate for such a straight-forward auth bug. But you _could_, potentially, in some circumstances, write a test in the course of TDD that tests a private function or method. We might call this a sub-unit test.

Aside from legacy, untested systems, there are also times when it’s just easier, or even necessary, to do an integration test as part of your TDD cycle. Particularly if you’re in the process of writing _an integration_.

As another example, [Behavior-Driven Development](https://dannorth.net/introducing-bdd/), a particular interpretation/approach of TDD, often exclusively uses E2E or UI tests. These tests are often coupled with lower-level tests, of course.

So in summary, TDD and “Unit tests” are orthogonal concepts. Many TDD tests happen to be unit tests, but that’s almost incidental, not by design or necessity.

* * *
_If you enjoyed this message, [subscribe](https://jhall.io/daily) to <u>The Daily Commit</u> to get future messages to your inbox._