The following are the main ideas and links to some info and terminology that helped me understand the test double concept.
A Test Double ...
… is the answer to the questions:
- How can we verify logic independently when code it depends on is unusable?
- How can we avoid Slow Tests?
… replaces a component on what a system under test (SUT) depends with a “test-specific equivalent”. here is a great visual representation regarding:
… gets its name from the notion of a stunt double.
“When the movie industry wants to film something that is potentially risky or dangerous for the leading actor to carry out, they hire a "stunt double" to take the place of the actor in the scene. The stunt double is a highly trained individual who is capable of meeting the specific requirements of the scene. They may not be able to act, but they know how to fall from great heights, crash a car, or whatever the scene calls for. How closely the stunt double needs to resemble the actor depends on the nature of the scene. Usually, things can be arranged such that someone who vaguely resembles the actor in stature can take their place.”
… works like this:
“For testing purposes, we can replace the real DOC (not the SUT!) with our equivalent of the "stunt double": the Test Double. During the fixture setup phase of our Four-Phase Test, we replace the real DOC with our Test Double. Depending on the kind of test we are executing, we may hard-code the behavior of the Test Double or we may configure it during the setup phase. When the SUT interacts with the Test Double, it won't be aware that it isn't talking to the real McCoy, but we will have achieved our goal of making impossible tests possible.”
… has a few different circumstances in which to use a particular type for our tests:
• If we have an Untested Requirement that we cannot verify because neither the SUT nor its DOCs provide an observation point for the SUT's indirect output that we need to verify.
• If we have Untested Code and a DOC does not provide the control point to allow us to exercise the SUT with the necessary indirect inputs.
• If we have Slow Tests and we want to be able to run our tests more quickly and hence more often.
… has the following hierarchical representation and role descriptions of its main flavors:
Purpose: Attribute or Method Parameter
Injects indirect inputs into SUT: no, never called
Handles indirect outputs of SUT: no, never called
Values provided by test(er): no
Test Stub
Purpose: Verify indirect inputs of SUT
Injects indirect inputs into SUT: yes
Handles indirect outputs of SUT: ignores them
Values provided by test(er): inputs
Purpose: Verify indirect outputs of SUT
Injects indirect inputs into SUT: optional
Handles indirect outputs of SUT: captures them for later verification
Values provided by test(er): inputs (optional)
Purpose: Verify indirect outputs of SUT
Injects indirect inputs into SUT: optional
Handles indirect outputs of SUT: verifies correctness against expectations
Values provided by test(er): outputs & inputs (optional)
Purpose: Run (unrunnable) tests (faster)
Injects indirect inputs into SUT: no
Handles indirect outputs of SUT: uses them
Values provided by test(er): none
Purpose: Stand in for procedural code not yet written
Injects indirect inputs into SUT: no
Handles indirect outputs of SUT: uses them
Values provided by test(er): none