Stephen A. Fuqua (saf)

a Bahá'í, software engineer, and nature lover in Austin, Texas, USA

Test Driven Development

Thinking Like QA

When approaching any kind of testing, every developer needs to start thinking like a tester. There are three essential questions to ask yourself when evaluating a system under test:

  1. what are the expected inputs?
  2. What are the expected outputs?
  3. What are the dependencies?

Develop test cases to explore a reasonable set of inputs. Build test cases that explore boundary conditions:

  1. Does the system behave as expected with null input?
  2. Does the system behave as expected with max value input (e.g. largest integer, a string that is longer than expected, etc.)
  3. Does the system behave as expected when a dependency does not behave well?

Red-Green-Refactor

This is the classic mantra of test-driven development (TDD):

Write a test that fails

Write code that passes the test

Cleanup the code so that it is easy to understand and maintain.

Note the emphasis on writing a test first. Why is that?

  1. Forces you to think clearly about expected inputs, outputs, and dependencies.
  2. Lets you iteratively build code that satisfies those different conditions, instead of trying to tackle the entire problem at once.
  3. Allows you to safely refactor, knowing that the test(s) will catch any errors.
  4. Typically results in better OO code, more closely adhering to SOLID principles.

Introducing TDD Into Untested Code

Q: How do you go about doing test-first development in legacy code that does not already have unit tests?

A: By changing to “Green-red-green-refactor”. That is, write a test that the current code passes. Then modify the test to reflect new requirements, thus making the current code fail the test.

If the code is not well-structured, this can be awfully hard to do. For tips on safely restructuring your code to facilitate g-r-g-r style testing, see Legacy Refactoring.

Arrange-Act-Assert

Within any given unit test, you’ll have three sections of code, frequently described by the terms:

  • Arrange: set up the inputs to the system, including dependencies. (INPUTS & DEPENDENCIES)
  • Act: run the system under test. (SYSTEM UNDER TEST)
  • Assert: verify that the results are correct, and potentially verifies that dependencies were used correctly. (OUTPUTS)

To improve unit test readability, it is often helpful to use these terms directly in the tests.

Behavior-Driven Development

Behavior-Driven Development, or BDD for short, is a variation on TDD that is focused on describing the application’s behavior in business terminology. Often times it is used with integration and acceptance tests, using real-world language based approach to describe the testing conditions (the inputs, system, and outputs). The technique of “given-when-then” is used to write a plain-(English, Spanish, etc) description of the system behavior:

Given some input condition   And another input condition  When some user or system takes an action  Then some expected output   And another expected output

It should be clear that this is a reformulation of Arrange-Act-Assert. This language can be useful in TDD, especially when structuring test classes and methods so that the test output report is meaningful to non-developers. When writing tests as nested classes, I like to keep the action (which is invariant) in a parent class, changing the order to When-Given-Then. In ReSharper, I thus getting something like this:

Example When-Given-Then test results


Back to the introduction / table of contents

Posted with : Software Testing, General Programming, General Programming