Definitions
DOC: Depended On Component
SUT: System Under Test
Why we need Test Doubles
In unit testing, there are circumstances that we need some part
of codes acts as we want. We have an Asset section in our Tests, in
which we want to evaluate the state or behavior of the SUT based on
the Arrange Section and Act section.
As this SUT depends on other components (that we are not
interested to test them inside this test), the state of these
components can change the behavior of the SUT. For example,
sometimes need the DOC throw exception so that the error path can
be tested. In this case DOC called the indirect
input of the test or a Control Point.
Sometimes in Test Assertion we have a method under test that
returns nothing or at least nothing that can be used to determine
whether it has performed its function correctly. In this situation
we need something to find out the state or behavior of the SUT.
That is the indirect output of the test or an
Observation Point
Indirect Inputs
Types of indirect inputs:
- return values of methods/functions
- values of update-able arguments
- Exceptions that could be thrown
Sometimes test can setup the DOC to respond to requests
specifically. For example, if DOC provides access to DB then
we can use BackDoor Setup and put specific values to DB and cause
the DOC respond in desired ways. in this case DB itself is the
control point!
There are a lot of cases we can not use the real component as
the control point:
- real component can not be manipulated
- its not cost effective to manipulate the real component
- manipulating the real component could have unacceptable side
effects
- the real component is not yet available
So we need something that we can control : a test double! (eg.
test Stub=allows us to force the SUT through all its code
paths)
Indirect Output
Sometimes can use DOC as an observation point:
- Asking the file system for the content of file that SUT has
written!
- ask db the content of the table that SUT updated
if upper methods or others fail we may need to replace the real
component with a test specific alternative. for example when:
- the calls to(or internal state of) the DOC cannot be
queried
- Query can be done but is not cost effective
- Query can be done but has unacceptable side effects
- the real component is not yet available
There are two types of replacement for DOC:
- Procedural behavioral Verification: Captures
calls during SUT execution and then compares them with the expected
calls after the SUT finished executing. (Test Spy+ Assertion
Methods)
- Expected behavior: Building a behavior
specification during the fixture setup and the comparing the actual
behavior with this expected behavior (Mock Testing). Mock objects
received calls from the SUT and check them with expected call
sequences , if it receives unexpected calls it fails the test
immediately.
Types of Test Doubles
-
Dummy Object
A placeholder passed to the SUT as an argument (or an attribute of
an argument) but never used in the SUT.
-
Test Stub
That replace a real component on which the SUT depends on so the
test can control the indirect inputs of the SUT Allowing the test
to force the SUT down paths it might not otherwise exercise.
in procedural programming languages, there are two types of
procedural test stubs : 1. test stub implemented as a stand in for
a not yet written procedure. 2. an alternative implementation of
the existed procedure used in the program.
-
Test Spy
A test spy is a more powerful version of the Test Stub and is used
to verify the indirect outputs of the SUT by giving the test a way
to inspect the after finishing SUT.
-
Mock Object
Object replace the real component that SUT depends on and verifies
the indirect output and encourage testing based on behavior
verification.
-
Fake Object
An object that replace the functionality of the real DOC with an
alternative implementation of the same functionality. main
difference with Mock object and test spy is that it is neither
directly controlled nor observed by the test. it is used to replace
the real DOC functionality in test for reasons than verification of
indirect input or output. it does the same functionality of the DOC
in a much simpler way. the most re common reasons for using it are
the real DOC has not been build , it is too slow or not
available in the test environment. like encapsulating all database
access behind a persistence layer interface and replacing this
layer component with one that used in memory hash tables instead of
a real database.

How to install test dounbles
-
Dependency injection
The client tells the SUT witch DOC to use. type: Setter
Injection,Constructor Injection, Parameter Injection. in all of
these Test injects the Test double by one of these mechanism when
instantiating SUT and SUT used test double when needs to call
DOC.
-
Dependency Look-up
SUT delegates the construction or retrieval of the DOC to another
object. Types: Object Factory and Service Locator: the test
configures the Service Locator or Factory to return the Test double
when the SUT requests the DOC.
Retrofitting the Testability
-
Test specific Subclass
When none of this mechanism are not built into the SUT, our test
can instantiate a TEST specific Subclass of the SUT to add the DI
mechanism or override other methods of the SUT with Test specific
behavior.
Prerequisite: SUt must use Self Calls to
none private methods that implement any functionality we need to
override from the test.
Drawback: it is possible to accidentally to
override parts of the behavior we are intending to test.
-
Test Hook
The DOC or the calls to it within the SUT are modified. May lead to
test logic in production.
-
AOP
Installing the test double behavior by defining a test point cut
that matches the place where the SUT calls the DOC and we would
rather have it call the Test Double. We need AOP enabled
environment but generated code do not need to deploy to production
environment.
Other Use of Test Doubles
-
Endoscopic Testing
Passing in a Mock object as an argument to the method under test ad
verify certain behavior of SUT that are not always visible form
Outside.
-
Need Driven Development
Building and testing software layer by layer, starting at the outer
most layer before we have implemented the lower layer.
-
Speed up Fixture setup:
Reduce the run-time cost of Fresh Fixture setup. When SUT needs to
interact with other objects that are difficult to create because
have many dependency , a simple Test double can be created
instead of the complex object graph. When applied to networks of
entity it called Entity Chain Snipping)
-
Speeding Up Test Execution:
For example replacing the RDBMS with an in memory Face
Objects.
References
Significant Revisions