Getting Testy: Redux
This is the final post in a series about Unit Testing.
For the past four months, we’ve been talking about writing good developer tests. I feel like we’ve only scratched the surface of this subject, but the series is now coming to an end.
Here’s a quick summary of the posts in the series:
-
Introduction: Introduce the series and explain the title “Getting Testy”.
-
History and Mechanics: Discuss the history of TDD and BDD; introduce the idea of test doubles and discuss the various kinds of test doubles that exist.
-
Philosophy: Introduce several of the debates that exist within the developer testing community and where I stand on those issues. Introduce my testing philosophy and approach. Talk about the people that influenced my approach to testing.
-
Functions: Using the Coin Changer kata, talk about how to test simple functions: methods that operate only on their inputs and return their outputs.
-
Abstraction: Continuing with the Coin Changer kata, talk about abstraction in tests. Describe how to use abstraction well in order to reduce duplication in the tests while still keeping them understandable to the future reader.
-
Layers: Use Mike Cohn’s testing pyramid to discuss the various layers in a system and how much we should test at each layer.
-
Acceptance: Introduce the Currency Exchange application and talk about how to write good acceptance-level tests. We use Cucumber for this part.
-
Learning: Introduce the idea of a “learning test” as a tool to explore an unfamiliar class or API. We used learning tests to understand Ruby’s OptionParser class so we could test-drive the command-line interface of the Currency Exchange application.
-
Outside-In: Start test-driving the command-line interface of the Currency Exchange application from the outside using test doubles to represent lower layers of the application.
-
APIs Part 1: Continuing into the core of the Currency Exchange program, we use another test double to flesh out more of the application. Rather than calling directly to an external API, we use an internal wrapper that has an interface that matches what our program needs.
-
APIs Part 2: We choose an external service and implement our internal interface to call out to that service’s API. We use VCR to help us test our API wrapper.
-
Steps: Now that the application is built, we return to the acceptance tests and implement the Cucumber step definitions. We find a couple of integration bugs along the way. This completes the Currency Exchange example.
-
Why?: After showing lots of examples of the kinds of tests I write, this post talks about tests I don’t write. The main point is to always ask “Why?” about any test or assertion we make. Why do we care about this particular thing? Is it important to the application and the domain, or is it an internal implementation detail that might change?
-
Doubles: I introduced test doubles earlier in the series, but this post gets into some ideas about how to use them well and avoid some of the pitfalls that come from using them poorly.
-
Anti-Patterns: Discuss testing anti-patterns and the problems that arise from them. Using Tim Ottinger and Brett Schuchert’s FIRST acronym, I talk about better patterns to use.
-
Bonus: An extra post that points to a post on the Shyp Engineering blog that makes a great case for fast tests and shows some concrete ideas about how to speed up slow ones.
-
Legacy: Talk about how to test legacy code and how it is often necessary to break all of the rules of good testing just to get a piece of code under control. I also introduce Katrina Owen’s Approvals gem as a handy tool for testing legacy code in Ruby.
I hope you enjoyed this series and that you learned something from it. I’d love any feedback you have. Is there something I didn’t cover well enough? Do you disagree with anything? Did you learn something that will help you when you’re writing tests for your system?
In the next post, we’ll move on to some new topics.