Cultivating Quality Code - A Comprehensive Review and Notes on 'Growing Object-Oriented Software Guided by Tests'

Last updated Apr 16, 2024 Published Oct 4, 2021

The content here is under the Attribution 4.0 International (CC BY 4.0) license

Test-driven development has been a subject of interest on this blog for a few years now and the first pot to come around was the classic book from Kent Beck: Test Drive Development by example. More recently I also wrote about my TDD journey as I was trying to focus on what would be the next steps on that.

Following this discovery, I started reading the book “Growing object-oriented software guided by tests” [1] which as the title says focuses on the development of software guided by tests, the proposal is to use a double-loop TDD - From the bigger picture to the smallest part.

If I were to give a comparison between TDD by example and GOOS I would say that on one hand, TDD by Example, focuses on shifting the mindset for testing, approaching simple problems in a way that starts with test first. On the other hand, GOOS focuses on expanding this idea, focusing on testing applications end-to-end.

The notes in this post are just my thoughts about the GOOS book and also questions that came up during the process, I also use the book deliberately for exposing my ideas.

Enjoy!

Part I

The first part of the book dives into the TDD flow to give a common ground for the reader and also for the authors to build on the basics before getting the book subject in place.

In this first part the Red-Gree-Refactor [2] is introduced as well as the idea of feedback and practices that support change. The book is from 2009 and the agile mindset was already in place (business moves fast so we should adapt accordingly). Feedback is fundamental to support this mindset, TDD gives developers the way to move forward fast and with confidence - Extreme programming was born with that in mind.

Steve and Nat also worked on a brief introduction on jUnit and jMock (jMock even refers to GOOS book on the home page!), this is the foundation needed to at least get started with testing. Despite the great introduction in the book, chapter 3 is about the minimum required to know about the tools. Other books give in-depth explanations around junit1, hamcrest and mocks. All in all, I felt that it was in the book as a requirement, in the end, would be difficult to start writing assertions and mocking things out without at least a minimum understanding - therefore, for beginners, this could potentially be a point of attention, if the load is heavy on this part, take the time to learn a bit more about the tools, cognitive load is crucial when learning something new and it is often neglected in the industry projects.

Note on the tools: It’s been 12 years since the book was released. Aging in the world of software is not easy, things get updated and old versions stop working. For the tools or any references that I am using here, I am trying to suggest the latest version - even though they might not work with the book code examples anymore.

Part II

Part II is about getting the context again, starting from the TDD lifecycle, and object-oriented style and finishing up with mocking (just mock types that you own!).

Part I also goes into the introduction of TDD, here the introduction starts to get deeper, the authors introduce the idea of starting with an acceptance test and for the first time the walking skeleton is mentioned, then Alistair is quoted:

A “walking skeleton” is an implementation of the thinnest possible slice of real functionality that we can automatically build, deploy, and test end-to-end [Cockburn04].

Growing object oriented software guided by tests, Steve Freeman and Nat Price, pg 32

The idea of a walking skeleton2 is to improve feedback and start to think about testing first, not at the function level, but rather at the whole system. Starting with that will make the process of writing tests easier. The authors quoted a project that they worked on that had never been tested end-to-end. As a result, it was difficult to build end-to-end tests and usually took months to do so as the system is complex.

Key takeaway: building an application with testing in mind makes it easier to keep the application maintainable and testable in the long run

In this part, the double-loop TDD is stressed (in Part I this idea is already introduced) to keep following throughout the book, here onwards this idea is the one that is explored the most, the following picture was taken from the book, page 40:

TDD double loop

In my opinion, the walking skeleton is the most important bit for any application that is developed in a testing-oriented fashion. The skeleton is the foundation on which the project will be built, in other words, this is an architectural decision that makes clear that every bit in place is designed for testability. Not only at the unit level but also having a look at the application working as a whole.

Besides that, I would like to elaborate on the following bullet points extracted from the book:

  • Acceptance: Does the whole system work?
  • Integration: Does our code work against code we can’t change?
  • Unit: Do our objects do the right thing, are they convenient to work with?

Acceptance testing has different understanding from software practitioners and testers, as mentioned in the book, acceptance should be as end-to-end as possible. From my understanding, end-to-end means using whatever dependencies are needed, for instance: the database, a WebSocket server, a cache server and so on. Earlier on, in this blog I wrote about strategies to test legacy code that goes towards this goal, there I mentioned the test pyramid as a way to measure how to start writing the acceptance test then moving downwards the pyramid to reach the unit tests.

Now reading GOOS, I see similarities between the approach I wrote and the one the authors presented, the XMPP server is an external dependency (more on that in Part III) that is used on the acceptance test. For me, the clear message that this part gives is: feedback on the entire system is important and we should aim to work on that from the start. Faster feedback means quicker response to change and adapt if needed. Exercising the system end-to-end is a way to achieve that.

Part III

Personally, as we get our hands dirty in code and see the pieces fit together, in this part, the authors also share how they would approach the split of iteration for the system. Often I share the feeling that it’s not easy to deal with and get things well divided before starting to code.

TODO - list of iterations for the auction application, pg 80

Even though there are some notes from both authors regarding agile planning, I think what it proposes to do is to offer a common ground for what is next rather than how to plan.

Also, for this part, the authors elaborate even more on the idea of end-to-end tests, they start to build what would be an example of an application developed guided by tests taking into account everything that has been covered by previous parts. This part is where the “planning” comes into play, giving a better idea of where to start, as we already know that the walking skeleton, is the way to go, the authors emphasize how:

A common practice is to call this step iteration zero: “iteration” because the team still needs to time-box its activities and “zero” because it’s before functional development starts in iteration one. One important task for iteration zero is to use the walking skeleton to test-drive the initial architecture.

Growing object oriented software guided by tests, Steve Freeman and Nat Price, pg 83

This part is where the coding takes place, starting from how to approach the first test case (spoiler alert: from the acceptance, chapter 11, “Passing the first test”). Here is where at least for me things started to get a bit blurry, as I am not a Java developer who knows the ins and outs of swing. The idea, is to provide the minimum to make the test pass, enabling feedback, as in the author’s words: “The point is to design and validate the initial structure of the end-to-end system”.

Closing up

Despite the book age (10+ years), if I were to start a project today, I would follow the same idea, enabling feedback of the system end-to-end and probably I would use docker to increase the reproducibility setup in other’s people computers.

Appendix

  • I found one thing that is interesting to link, this GitHub project that recreates the auction system step by step via commits.
  • This blog post is a kind of good sum up of the book, besides that the author also gives his point of view and the “bad parts” of the book. I think that this post could be a reference for people who don’t have time to read the book.

A note on outside-in TDD

When I am coding, usually I prefer the London school. Practicing outside-in TDD is my go-to for any exercise or professional work. I even built a json-tool that uses outside-in with cypress + react testing library. Anyway, it wasn’t before I gave GOOS a read, that I started to ask myself, was GOOS book the precursor for the different schools of TDD?

Such a question came up, as I went by the book and saw this statement:

This slice is so minimal that we’re not even concerned with sending a bid; we just want to know that the two sides can communicate and that we can test the system from outside (through the client’s GUI and by injecting events as if from the external auction server).

Growing object oriented software guided by tests, Steve Freeman and Nat Price, pg 84

References

  1. [1]S. Freeman and N. Pryce, Growing object-oriented software, guided by tests. Pearson Education, 2009.
  2. [2]K. Beck, TDD by example. Addison-Wesley Professional, 2000.

Footnotes

  1. jUnit in action https://www.manning.com/books/junit-in-action-third-edition 

  2. Even though the walking skeleton in the book is in Java, I also wrote a note on that, I proposed a walking skeleton with javascript and “modern” tools.