Growing Object Oriented Software, Guided By Tests (chapter 1)

Feb 03, 22

Here is the start of muh book report.

Chapter 1

TDD basic cycle:

  1. Write the test
  2. Write enough code to pass the test
  3. Refactor

The Golden Rule Of TDD: Never write new functionality without a failing test.

Initial process:

  • Write a failing acceptance test
    • Write a failing unit test
    • Write enough code to make the test pass
    • Refactor
    • repeat the above until the acceptance test passes (feature complete)
  • Acceptance test passes!
  • Release feature

We start with a failing acceptance (feature) test, to show us at a high level what the feature should achieve. We then use TDD basic with unit testing to make the above feature functional. The basic shape is 1 outer loop (feature test) with many internal loops (unit tests).

Never commit failing unit tests to source control

The feature test should not call the internals of the code, it should test the high level feature. Think about the feature test as a way your API will be consumed. It should be like a black box, with an interface.

We test the interface of the black box.

Inside the black box is the code that is covered by unit tests.

Acceptance/feature tests will be relatively slow - unit tests should be lightning fast to allow a fast, iterative approach.

The classic red, green, refactor.

Levels of testing

Acceptance: Does the whole system work (should be ran as part of the deployment process)

Integration: Does our code work against code we can’t change?

Unit: Do our internal objects do the right thing, and are they easy to work with?

External vs. Internal quality

External quality is what the customer / user expects of the software. Is it functional, responsive, available etc? Internal quality is the quality of the code base. Biggest factor: is the code easy to change in line with fluid requirements?

  • We can use acceptance / end-to-end testing to check the external quality, and also confirm that we have understood the requirements from the customer.

  • We can use unit testing to confirm that our code base is clean - ie highly cohesive and loosely coupled. See earlier point about being easy to change ;)

However unit tests do not exercise the full system, thus acceptance tests are vital to confirm that the system functions as expected by the user / customer / consumer. Are we feature complete?

A note on coupling and cohesion:

Coupling

When objects are tightly coupled together, they make the system brittle and hard to change. We should strive to use tests to reduce coupling. Let us be guided by the tests 🙏

see: SOLID principles et al

Cohesion

An object be said to be cohesive when all of it’s parts form a meaningful unit.

A couple of examples:

  • An element that parses URL’s and dates would have low cohesion as it’s responsibilities are unfocused
  • An element that only parses punctuation in a URL would be low cohesion as it is too focused on one thing
  • An element that parses URL’s, checks their punctuation, calls an API and then saves the data would be highly cohesive as it is focused on performing a task.

A good example of low cohesion would be a washing machine that washes clothes and dishes. It will probably do both things poorly, and the user would end up with fucked up clothes and broken plates.

A good example of high cohesion would be a washing machine that washes clothes, and also dries them. A user could take care of their whole washing cycle in one go - noice.

fin of chapter 1

p.s I think the blog is feeling a little unstructured in this linear format - I will investigate a new layout by category. Basically I want muh vim tips at the top 😏