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:
- what are the expected inputs?
- What are the expected outputs?
- What are the dependencies?
Develop test cases to explore a reasonable set of inputs. Build test cases that explore boundary conditions:
- Does the system behave as expected with null input?
- Does the system behave as expected with max value input (e.g. largest integer, a string that is longer than expected, etc.)
- 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?
- Forces you to think clearly about expected inputs, outputs, and dependencies.
- Lets you iteratively build code that satisfies those different conditions, instead of trying to tackle the entire problem at once.
- Allows you to safely refactor, knowing that the test(s) will catch any errors.
- 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:
Back to the introduction / table of contents
Posted with : Software Testing, General Programming, General Programming