One obvious solution is to put all tests that depend on the same starting state into the same Testcase Class page 373 and to create the SUT in the appropri-ate stappropri-ate in the set
Trang 1public void testStatus_initial() {
// in-line setup
Airport departureAirport = new Airport("Calgary", "YYC");
Airport destinationAirport = new Airport("Toronto", "YYZ");
Flight flight = new Flight(flightNumber,
Airport departureAirport = new Airport("Calgary", "YYC");
Airport destinationAirport = new Airport("Toronto", "YYZ");
Flight flight = new Flight( flightNumber,
departureAirport,
destinationAirport);
flight.cancel(); // still part of setup
// Exercise SUT and verify outcome
We can refactor the fi xture setup logic by using an Extract Method refactoring
to remove any frequently repeated code sequences into utility methods with
Intent-Revealing Names We leave the calls to the methods in the test, however,
so that the reader can see what is being done The method calls that remain
within the test will convey the “big picture” to the test reader The utility
meth-od bmeth-odies contain the irrelevant mechanics of carrying out the intent If we need
to share the Delegated Setups with another Testcase Class, we can use either a
Pull Up Method [Fowler] refactoring to move them to a Testcase Superclass or
a Move Method [Fowler] refactoring to move them to a Test Helper class
Example: Delegated Setup
In this version of the test, we use a method that hides the fact that we need two
airports instead of creating the two airports needed by the fl ight within each Test
Method We could produce this version of the tests either through refactoring or
by writing the test in this intent-revealing style right off the bat
Delegated Setup
Trang 2public void testGetStatus_initial() {
// setup
Flight flight = createAnonymousFlight();
// exercise SUT and verify outcome
Flight flight = createAnonymousCancelledFlight();
// exercise SUT and verify outcome
assertEquals(FlightState.CANCELLED, flight.getStatus());
// teardown
// garbage-collected
}
The simplicity of these tests was made possible by the following Creation Methods,
which hide the “necessary but irrelevant” steps from the test reader:
private int uniqueFlightNumber = 2000;
public Flight createAnonymousFlight(){
Airport departureAirport = new Airport("Calgary", "YYC");
Airport destinationAirport = new Airport("Toronto", "YYZ");
public Flight createAnonymousCancelledFlight(){
Flight flight = createAnonymousFlight();
Trang 3Creation Method
How do we construct the Fresh Fixture?
We set up the test fi xture by calling methods that hide the mechanics of
building ready-to-use objects behind Intent-Revealing Names
Fixture setup usually involves the creation of a number of objects In many
cases, the details of those objects (i.e., the attribute values) are unimportant but
must be specifi ed to satisfy each object’s constructor method Including all of
this unnecessary complexity within the fi xture setup part of the test can lead to
Obscure Tests (page 186) and certainly doesn’t help us achieve Tests as
Docu-mentation (see page 23)!
How can a properly initialized object be created without having to clutter
the test with In-line Setup (page 408)? The answer, of course, is to encapsulate
this complexity Delegated Setup (page 411) moves the mechanics of the fi xture
setup into other methods but leaves overall control and coordination within
the test itself But what to delegate to? A Creation Method is one way we can
encapsulate the mechanics of object creation so that irrelevant details do not
distract the reader
Fixture Setup
Fixture Setup
Creation Method
Trang 4How It Works
As we write tests, we don’t bother asking whether a desired utility function
exists; we just use it! (It helps to pretend that we have a loyal helper sitting
next to us who will quickly fi ll in the bodies of any functions we call that do
not exist as yet.) We write our tests in terms of these magic functions with
Intent-Revealing Names [SBPP], passing as parameters only those things that
will be verifi ed in the assertions or that should affect the outcome of the test
Once we’ve written the test in this very intent-revealing style, we must implement all of the magic functions that we’ve been calling The functions that create objects
are our Creation Methods; they encapsulate the complexity of object creation The
simple ones call the appropriate constructor, passing it suitable default values for
anything needed but not supplied as a parameter If any of the constructor
argu-ments are other objects, the Creation Method will fi rst create those depended-on
objects before calling the constructor
The Creation Method may be placed in all the same places where we put
Test Utility Methods (page 599) As usual, the decision is based on the expected
scope of reuse and the Creation Method’s dependencies on the API of the SUT
A related pattern is Object Mother (see Test Helper on page 643), which is a
combination of Creation Method, Test Helper, and optionally Automated
Tear-down (page 503)
When to Use It
We should use a Creation Method whenever constructing a Fresh Fixture
(page 311) requires signifi cant complexity and we value Tests as Documentation.
Another key indicator for using Creation Method is that we are building the
system in a highly incremental way and we expect the API of the system (and
especially the object constructors) to change frequently Encapsulating
knowl-edge of how to create a fi xture object is a special case of SUT API Encapsulation
(see Test Utility Method), and it helps us avoid both Fragile Tests (page 239) and
Obscure Tests.
The main drawback of a Creation Method is that it creates another API for
test automaters to learn This isn’t much of a problem for the initial test
devel-opers because they are typically involved in building this API but it can create
“one more thing” for new additions to the team to learn Even so, this API
should be pretty easy to understand because it is just a set of Factory Methods
[GOF] organized in some way
If we are using a Prebuilt Fixture (page 429), we should use Finder Methods (see Test Utility Method) to locate the prebuilt objects At the same time, we
Creation
Method
Trang 5may still use Creation Methods to lay mutable objects that we plan to modify
on top of an Immutable Shared Fixture (see Shared Fixture on page 317).
Several variations of Creation Method are worth exploring
Variation: Parameterized Creation Method
While it is possible (and often very desirable) for Creation Methods to take no
parameters whatsoever, many tests will require some customization of the
cre-ated object A Parameterized Creation Method allows the test to pass in some
attributes to be used in the creation of the object In such a case, we should pass
only those attributes that are expected to affect (or those we want to
demon-strate do not affect) the test’s outcome; otherwise, we could be headed down the
slippery slope to Obscure Tests.
Variation: Anonymous Creation Method
An Anonymous Creation Method automatically creates a Distinct Generated
Value (see Generated Value on page 723) as the unique identifi er for the object it
is creating even though the arguments it receives may not be unique This
behav-ior is invaluable for avoiding Unrepeatable Tests (see Erratic Test on page 228)
because it ensures that every object we create is unique, even across multiple test
runs If the test cares about some attributes of the object to be created, it can
pass them as parameters of the Creation Method; this behavior turns the
Anony-mous Creation Method into a Parameterized AnonyAnony-mous Creation Method.
Variation: Parameterized Anonymous Creation Method
A Parameterized Anonymous Creation Method is a combination of several other
variations of Creation Method in that we pass in some attributes to be used in
the creation of the object but let the Creation Method create the unique
identi-fi er for it A Creation Method could also take zero parameters if the test doesn’t
care about any of the attributes
Variation: Named State Reaching Method
Some SUTs are essentially stateless, meaning we can call any method at any
time By contrast, when the SUT is state-rich and the validity or behavior of
methods is affected by the state of the SUT, it is important to test each method
from each possible starting state We could chain a bunch of such tests together
in a single Test Method (page 348), but that approach would create an Eager
Test (see Assertion Roulette on page 224) It is better to use a series of
Single-Condition Tests (see page 45) for this purpose Unfortunately, that leaves us
Creation Method
Trang 6with the problem of how to set up the starting state in each test without a lot of
Test Code Duplication (page 213)
One obvious solution is to put all tests that depend on the same starting state
into the same Testcase Class (page 373) and to create the SUT in the
appropri-ate stappropri-ate in the setUp method using Implicit Setup (page 424) (called Testcase
Class per Fixture; see page 631) The alternative is to use Delegated Setup by
calling a Named State Reaching Method; this approach allows us to choose
some other way to organize our Testcase Classes.
Either way, the code that sets up the SUT will be easier to understand if it
is short and sweet That’s where a Named State Reaching Method comes in
handy By encapsulating the logic required to create the test objects in the
cor-rect state in a single place (whether on the Testcase Class or a Test Helper), we
reduce the amount of code we must update if we need to change how we put
the test object into that state
Variation: Attachment Method
Suppose we already have a test object and we want to modify it in some way We
fi nd ourselves performing this task in enough tests to want to code this modifi
ca-tion once and only once The soluca-tion in this case is an Attachment Method The
main difference between this variation and the original Creation Method pattern
is that we pass in the object to be modifi ed (one that was probably returned by
another Creation Method) and the object we want to set one of its attributes to;
the Attachment Method does the rest of the work for us
Implementation Notes
Most Creation Methods are created by doing an Extract Method [Fowler]
refac-toring on parts of an existing test When we write tests in an “outside-in”
man-ner, we assume that the Creation Methods already exist and fi ll in the method
bodies later In effect, we defi ne a Higher-Level Language (see page 41) for defi
n-ing our fi xtures Nevertheless, there is another, completely different way to defi ne
Creation Methods.
Variation: Reuse Test for Fixture Setup
We can set up the fi xture by calling another Test Method to do the fi xture setup
for us This assumes that we have some way of accessing the fi xture that the
other test created, either through a Registry [PEAA] object or through instance
variables of the Testcase Object (page 382)
It may be appropriate to implement a Creation Method in this way when
we already have tests that depend on other tests to set up their test fi xture but
Creation
Method
Trang 7we want to reduce the likelihood that a change in the test execution order of
Chained Tests (page 454) will cause tests to fail Mind you, the tests will run
more slowly because each test will call all the preceding tests it depends on each
time each test is run rather than each test being run only once per test run Of
course, each test needs to call only the specifi c tests it actually depends on, not all
tests in the test suite This slowdown won’t be very noticeable if we have replaced
any slow components, such as a database, with a Fake Object (page 551)
Wrapping the Test Method in a Creation Method is a better option than
calling the Test Method directly from the client Test Method because most Test
Methods are named based on which test condition(s) they verify, not what (fi
x-ture) they leave behind The Creation Method lets us put a nice Intent-Revealing
Name between the client Test Method and the implementing Test Method It
also solves the Lonely Test (see Erratic Test) problem because the other test is
run explicitly from within the calling test rather than just assuming that it was
already run This scheme makes the test less fragile and easier to understand but
it won’t solve the Interacting Tests (see Erratic Test) problem: If the test we call
fails and leaves the test fi xture in a different state than we expected, our test will
likely fail as well, even if the functionality we are testing is still working
Motivating Example
In the following example, the testPurchase test requires a Customer to fi ll the role of
thebuyer The fi rst and last names of the buyer have no bearing on the act of
pur-chasing, but are required parameters of the Customer constructor; we do care that
theCustomer’s credit rating is good (“G”) and that he or she is currently active
public void testPurchase_firstPurchase_ICC() {
The use of constructors in tests can be problematic, especially when we are
building an application incrementally Every change to the parameters of the
constructor will force us to revisit a lot of tests or jump through hoops to keep
the constructor signatures backward compatible for the sake of the tests
Creation Method
Trang 8Refactoring Notes
We can use an Extract Method refactoring to remove the direct call to the
construc-tor We can give the new Creation Method an appropriate Intent-Revealing Name
such as createCustomer based on the style of Creation Method we have created
Example: Anonymous Creation Method
In the following example, instead of making that direct call to the Customer
constructor, we now use the CustomerCreation Method Notice that the coupling
between the fi xture setup code and the constructor has been removed If another
parameter such as phone number is added to the Customer constructor, only the
CustomerCreation Method must be updated to provide a default value; the fi xture
setup code remains insulated from the change thanks to encapsulation
public void testPurchase_firstPurchase_ACM() {
Customer buyer = createAnonymousCustomer();
//
}
public void testPurchase_subsequentPurchase_ACM() {
Customer buyer = createAnonymousCustomer();
//
}
We call this pattern an Anonymous Creation Method because the identity of
the customer does not matter The Anonymous Creation Method might look
something like this:
public Customer createAnonymousCustomer() {
int uniqueid = getUniqueCustomerId();
return new Customer(uniqueid,
"FirstName" + uniqueid,
"LastName" + uniqueid,
"G", "ACTIVE");
}
Note the use of a Distinct Generated Value to ensure that each anonymous Customer
is slightly different to avoid accidentally creating an identical Customer
Example: Parameterized Creation Method
If we wanted to supply some of the Customer’s attributes as parameters, we could
defi ne a Parameterized Creation Method:
public void testPurchase_firstPurchase_PCM() {
Customer buyer =
createCreditworthyCustomer("FirstName", "LastName");
Creation
Method
Trang 9//
}
public void testPurchase_subsequentPurchase_PCM() {
Customer buyer = createCreditworthyCustomer("FirstName", "LastName");
//
}
Here’s the corresponding Parameterized Creation Method defi nition:
public Customer createCreditworthyCustomer(
String firstName, String lastName) {
int uniqueid = getUniqueCustomerId();
Example: Attachment Method
Here’s an example of a test that uses an Attachment Method to associate two
customers to verify that both get the best discount either of them has earned or
negotiated:
public void testPurchase_relatedCustomerDiscount_AM() {
Customer buyer = createCreditworthyCustomer("Related", "Buyer");
Although this example is relatively simple, the call to this method is still easier to
understand than reading both the method calls of which it consists
Creation Method
Trang 10Example: Test Reused for Fixture Setup
We can reuse other tests to set up the fi xture for our test Here is an example of
how not to do it:
private Customer buyer;
private AccountManager sut = new AccountManager();
private Account account;
public void testCustomerConstructor_SRT() {
public void testPurchase_SRT() {
testCustomerConstructor_SRT(); // Leaves in field "buyer"
account = sut.createAccountForCustomer( buyer );
assertEquals( buyer.name, account.customerName, "cust");
//
}
The problem here is twofold First, the name of the Test Method we are calling
describes what it verifi es (e.g., a name) and not what it leaves behind (i.e., a Customer
in the buyer fi eld Second, the test does not return a Customer; it leaves the Customer in
an instance variable This scheme works only because the Test Method we want
to reuse is on the same Testcase Class; if it were on an unrelated class, we would
have to do a few backfl ips to access the buyer A better way to accomplish this goal
is to encapsulate this call behind a Creation Method:
private Customer buyer;
private AccountManager sut = new AccountManager();
private Account account;
public void testCustomerConstructor_RTCM() {
account = sut.createAccountForCustomer( buyer );
assertEquals( buyer.name, account.customerName, "cust");
Trang 11return buyer;
//
}
Notice how much more readable this test has become? We can see where the buyer
came from! This was easy to do because both Test Methods were on the same
class If they were on different classes, our Creation Method would have to create
an instance of the other Testcase Class before it could run the test Then it would
have to fi nd a way to access the buyer instance variable so that it could return it
Method
Trang 12Implicit Setup
How do we construct the Fresh Fixture?
We build the test fi xture common to several tests in the setUp method.
To execute an automated test, we require a text fi xture that is well understood
and completely deterministic We are using a Fresh Fixture (page 311) approach
to build the Minimal Fixture (page 302) for the use of this one test
Implicit Setup is a way to reuse the fi xture setup code for all Test ods (page 348) in a Testcase Class (page 373)
Meth-How It Works
All tests in a Testcase Class create identical Fresh Fixtures by doing test fi xture
setup in a special setUp method on the Testcase Class The setUp method is called
automatically by the Test Automation Framework (page 298) before it calls each
Test Method This allows the fi xture setup code placed in the setUp method to
be reused without reusing the same instance of the test fi xture This approach is
called “implicit” setup because the calls to the fi xture setup logic are not explicit
within the Test Method, unlike with In-line Setup (page 408) and Delegated
Trang 13When to Use It
We can use Implicit Setup when several Test Methods on the same Testcase Class
need an identical Fresh Fixture If all Test Methods need the exact same fi xture,
then the entire Minimal Fixture needed by each test can be set up in the setUp
method This form of Test Method organization is known as Testcase Class per
Fixture (page 631)
When the Test Methods need different fi xtures because we are using a Testcase
Class per Feature (page 624) or Testcase Class per Class (page 617) scheme, it
is more diffi cult to use Implicit Setup and still build a Minimal Fixture We can
use the setUp method only to set up the part of the fi xture that does not cause any
problems for the other tests A reasonable compromise is to use Implicit Setup to
set up the parts of the fi xture that are essential but irrelevant and leave the setup
of critical (and different from test to test) parts of the fi xture to the individual
Test Methods Examples of “essential but irrelevant” fi xture setup include
ini-tializing variables with “don’t care” values and iniini-tializing hidden “plumbing”
such as database connections Fixture setup logic that directly affects the state of
the SUT should be left to the individual Test Methods unless every Test Method
requires the same starting state
The obvious alternatives for creating a Fresh Fixture are In-line Setup, in
which we include all setup logic within each Test Method without factoring out
any common code, and Delegated Setup, in which we move all common fi xture
setup code into a set of Creation Methods (page 415) that we can call from
within the setup part of each Test Method
Implicit Setup removes a lot of Test Code Duplication (page 213) and helps
prevent Fragile Tests (page 239) by moving much of the nonessential interaction
with the SUT out of the very numerous tests and into a much smaller
num-ber of places where it is easier to maintain It can, however, lead to Obscure
Tests (page 186) when a Mystery Guest makes the test fi xture used by each test
less obvious It can also lead to a Fragile Fixture (see Fragile Test) if all tests in
the class do not really need identical test fi xtures
Implementation Notes
The main implementation considerations for Implicit Setup are as follows:
• How do we cause the fi xture setUp method to be called?
• How do we tear the fi xture down?
• How do the Test Methods access the fi xture?
Implicit Setup
Trang 14Calling the Setup Code
AsetUp method is the most common way to handle Implicit Setup; it consists of
having the Test Automation Framework call the setUp method before each Test
Method Strictly speaking, the setUp method is not the only form of implicit fi
x-ture setup Suite Fixx-ture Setup (page 441), for example, is used to set up and tear
down a Shared Fixture (page 317) that is reused by the Test Methods on a single
Testcase Class In addition, Setup Decorator (page 447) moves the setUp method
to a Decorator [GOF] object installed between the Test Suite Object (page 387)
and the Test Runner (page 377) Both are forms of Implicit Setup because the
setUp logic is not explicit within the Test Method.
Tearing Down the Fixture
The fi xture teardown counterpart of Implicit Setup is Implicit Teardown (page 516)
Anything that we set up in the setUp method that is not automatically cleaned up by
Automated Teardown (page 503) or garbage collection should be torn down in the
correspondingtearDown method
Accessing the Fixture
The Test Methods need to be able to access the test fi xture built in the
suffi cient To communicate between the setUp method and the Test Method,
how-ever, the local variables must be changed into instance variables We must be
careful not to make them class variables as this will result in the potential for a
Shared Fixture (See the sidebar “There’s Always an Exception” on page 384 for a
description of when instance variations do not provide this level of isolation.)
Airport departureAirport = new Airport("Calgary", "YYC");
Airport destinationAirport = new Airport("Toronto", "YYZ");
Flight flight = new Flight( flightNumber,
Trang 15}
public void testStatus_cancelled() {
// in-line setup
Airport departureAirport = new Airport("Calgary", "YYC");
Airport destinationAirport = new Airport("Toronto", "YYZ");
Flight flight = new Flight( flightNumber,
departureAirport,
destinationAirport);
flight.cancel(); // still part of setup
// exercise SUT and verify outcome
These tests contain a fair amount of Test Code Duplication We can remove this
duplication by refactoring this Testcase Class to use Implicit Setup There are
two refactoring cases to consider
First, when we discover that all tests are doing similar work to set up their
test fi xtures but are not sharing a setUp method, we can do an Extract
Meth-od [Fowler] refactoring of the fi xture setup logic in one of the tests to create
oursetUp method We will also need to convert any local variables to instance
variables (fi elds) that hold the references to the resulting fi xture until the Test
Method can access it
Second, when we discover that a Testcase Class already uses the setUp method to
build the fi xture and has tests that need a different fi xture, we can use an Extract
Class [Fowler] refactoring to move all Test Methods that need a different setup
method to a different class We need to ensure any instance variables that are used
to convey knowledge of the fi xture from the setup method to the Test Methods
are transferred along with the setUp method Sometimes it is simpler to clone the
Testcase Class and delete each test from one or the other copy of the class; we can
then delete from each class any instance variables that are no longer being used
Example: Implicit Setup
In this modifi ed example, we have moved all common fi xture setup code to the
setUp method of our Testcase Class This avoids the need to repeat this code in
each test and makes each test much shorter—which is a good thing
Airport departureAirport;
Airport destinationAirport;
Implicit Setup
Trang 16Flight flight;
public void setUp() throws Exception{
super.setUp();
departureAirport = new Airport("Calgary", "YYC");
destinationAirport = new Airport("Toronto", "YYZ");
BigDecimal flightNumber = new BigDecimal("999");
flight = new Flight( flightNumber , departureAirport,
public void testGetStatus_cancelled() {
// implicit setup partially overridden
flight.cancel();
// exercise SUT and verify outcome
assertEquals(FlightState.CANCELLED, flight.getStatus());
}
This approach has several disadvantages, which arise because we are not
organizing our Test Methods around a Testcase Class per Fixture (We are using
Testcase Class per Feature here.) All the Test Methods on the Testcase Class
must be able to make do with the same fi xture (at least as a starting point), as
evidenced by the partially overridden fi xture setup in the second test in the
exam-ple The fi xture is also not very obvious in these tests Where does the fl ight come
from? Is there anything special about it? We cannot even rename the instance
variable to communicate the nature of the fl ight better because we are using it to
hold fl ights with different characteristics in each test
Implicit
Setup
Trang 17Prebuilt Fixture
How do we cause the Shared Fixture to be built before the fi rst
test method that needs it?
We build the Shared Fixture separately from running the tests.
When we choose to use a Shared Fixture (page 317), whether it be for reasons
of convenience or out of necessity, we need to create the Shared Fixture before
we use it
How It Works
We create the fi xture sometime before running the test suite We can create the
fi xture a number of different ways that we’ll discuss later The most important
point is that we don’t need to build the fi xture each time the test suite is run
because the fi xture outlives both the mechanism used to build it and any one
test run that uses it
When to Use It
We can reduce the overhead of creating a Shared Fixture each time a test suite is
run by creating the fi xture only occasionally This pattern is especially
appropri-ate when the cost of constructing the Shared Fixture is extremely high or cannot
be automated easily
Setup
Exercise Verify Teardown
Fixture
Exercise Verify Teardown
SUTTest Runner
Setup
Setup
Exercise Verify Teardown
Fixture
Exercise Verify Teardown
SUTTest Runner
Setup
Also known as:
Prebuilt Context, Test Bed
Prebuilt Fixture
Trang 18Because of the Manual Intervention (page 250) required to (re)build the fi xture
before the tests are run, we’ll probably end up using the same fi xture several times,
which can lead to Erratic Tests (page 228) caused by shared fi xture pollution We
may be able to avoid these problems by treating the Prebuilt Fixture as an
Immu-table Shared Fixture (see Shared Fixture) and building a Fresh Fixture (page 311)
for anything we plan to modify
The alternatives to a Prebuilt Fixture are a Shared Fixture that is built once
per test run and a Fresh Fixture Shared Fixtures can be constructed using Suite
Fixture Setup (page 441), Lazy Setup (page 435), or Setup Decorator (page 447)
Fresh Fixtures can be constructed using In-line Setup (page 408), Implicit
Setup (page 424), or Delegated Setup (page 411)
Variation: Global Fixture
A Global Fixture is a special case of Prebuilt Fixture where we shared the fi xture
between multiple test automaters The key difference is that the fi xture is globally
visible and not “private” to a particular user This pattern is most commonly
em-ployed when we are using a single shared Database Sandbox (page 650) without
using some form of Database Partitioning Scheme (see Database Sandbox)
The tests themselves can be the same as those used for a basic Prebuilt
Fix-ture; likewise, the fi xture setup is the same as that for a Prebuilt Fixture What’s
different here are the kinds of problems we can encounter Because the fi xture
is now shared among multiple users, each of whom is running a separate Test
Runner (page 377) on a different CPU, we may experience all sorts of
multipro-cessing-related issues The most common problem is a Test Run War (see Erratic
Test) where we see seemingly random results We can avoid this possibility
by adopting some kind of Database Partitioning Scheme or by using Distinct
Generated Values (see Generated Value on page 723) for any fi elds with unique
key constraints
Implementation Notes
The tests themselves look identical to a basic Shared Fixture What’s different is
how the fi xture is set up The test reader won’t be able to fi nd any sign of it either
within the Testcase Class (page 373) or in a Setup Decorator or Suite Fixture
Setup method Instead, the fi xture setup is most probably performed manually
via some kind of database copy operation, by using a Data Loader (see Back
Door Manipulation on page 327) or by running a database population script In
these examples of Back Door Setup (see Back Door Manipulation), we bypass
the SUT and interact with its database directly (See the sidebar “Database as
Prebuilt
Fixture
Trang 19SUT API?” on page 336 for an example of when the back door really is a front
door.) Another option is to use a Fixture Setup Testcase (see Chained Tests on
page 454) run from a Test Runner either manually or on a regular schedule
Another difference is how the Finder Methods (see Test Utility Method on
page 599) are implemented We cannot just store the results of creating the
objects in a class variable or an in-memory Test Fixture Registry (see Test Helper
on page 643) because we aren’t setting the fi xture up in code within the test
run Two of the more commonly used options available to us are (1) to store
the unique identifi ers generated during fi xture construction in a persistent Test
Fixture Registry (such as a fi le) as we build the fi xture so that the Finder
Meth-ods can retrieve them later and (2) to hard-code the identifi ers in the Finder
Methods We could search for objects/records that meet the Finder Methods’
criteria at runtime, but that approach might result in Nondeterministic Tests
(see Erratic Test) because each test run could end up using a different
object/re-cord from the Prebuilt Fixture This strategy may be a good idea if each test run
modifi es the objects such that they no longer satisfy the criteria Nevertheless, it
may make debugging a failing test rather diffi cult, especially if the failures occur
intermittently because some other attribute of the selected object is different
protected void tearDown() throws Exception {
// Cannot delete any objects because we don't know
// whether this is the last test
}
Note the call to setupStandardAirports in the setUp method The tests use this fi xture
by calling Finder Methods that return objects from the fi xture that match certain
criteria:
1 Of course, there are other ways to set up the Shared Fixture, such as Setup Decorator
and Suite Fixture Setup.
Prebuilt Fixture
Trang 20public void testGetFlightsByFromAirport_OneOutboundFlight()
One way to convert a Testcase Class from a Standard Fixture (page 305) to a
Prebuilt Fixture is to do an Extract Class [Fowler] refactoring so that the fi xture
is set up in one class and the Test Methods (page 348) are located in another
class Of course, we need to provide a way for the Finder Methods to
deter-mine which objects or records exist in the structure because we won’t be able to
guarantee that any instance or class variables will bridge the time gap between
fi xture construction and fi xture usage
Example: Prebuilt Fixture Test
Here is the resulting Testcase Class that contains the Test Methods Note that it
looks almost identical to the basic Shared Fixture tests.
public void testGetFlightsByFromAirport_OneOutboundFlight()
Trang 21assertOnly1FlightInDtoList( "Flights at origin",
Example: Fixture Setup Testcase
We may fi nd it to be convenient to set up our Prebuilt Fixture using xUnit This is
simple to do if we already have the appropriate Creation Methods (page 415) or
constructors already defi ned and we have a way to easily persist the objects into
the Database Sandbox In the following example, we call the same method as in
the previous example from the setUp method, except that now the method lives
in the setUp method of a Fixture Setup Testcase that can be run whenever we want
to regenerate the Prebuilt Fixture:
public class FlightManagementFacadeSetupTestcase
extends AbstractFlightManagementFacadeTestCase {
public FlightManagementFacadeSetupTestcase(String name) {
super(name);
}
protected void setUp() throws Exception {
facade = new FlightMgmtFacadeImpl();
helper = new FlightManagementTestHelper();
setupStandardAirportsAndFlights();
saveFixtureInformation();
}
protected void tearDown() throws Exception {
// Leave the Prebuilt Fixture for later use
}
}
Prebuilt Fixture
Trang 22Note that there are no Test Methods on this Testcase Class and the tearDown
method is empty Here we want to do only the setup—nothing else
Once we created the objects, we saved the information to the database using
the call to saveFixtureInformation; this method persists the objects and saves the
various keys in a fi le so that we can reload them for use from the subsequent
real test runs This approach avoids the need to hard-code knowledge of the
fi xture into Test Methods or Test Utility Methods In the interest of space, I’ll
spare you the details of how we fi nd the “dirty” objects and save the key
infor-mation; there is more than one way to handle this task and any of these tactics
will suffi ce
Example: Prebuilt Fixture Setup Using a Data Population
Script
There are as many ways to build a Prebuilt Fixture in a Database Sandbox as
there are programming languages—everything from SQL scripts to Pearl and
Ruby programs These scripts can contain the data or they can read the data
from a collection of fl at fi les We can even copy the contents of a “golden”
data-base into our Datadata-base Sandbox I’ll leave it as an exercise for you to fi gure out
what’s most appropriate in your particular circumstance
Prebuilt
Fixture
Trang 23Lazy Setup
How do we cause the Shared Fixture to be built before the
fi rst test method that needs it?
We use Lazy Initialization of the fi xture to create it in the fi rst test that needs it.
Shared Fixtures (page 317) are often used to speed up test execution by reducing
the number of times a complex fi xture needs to be created Unfortunately, a test
that depends on other tests to set up the fi xture cannot be run by itself; it is a
Lonely Test (see Erratic Test on page 228)
We can avoid this problem by having each test use Lazy Setup to set up the
fi xture if it is not already set up
How It Works
We use Lazy Initialization [SBPP] to construct the fi xture in the fi rst test that
needs it and then store a reference to the fi xture in a class variable that every
test can access All subsequently run tests will discover that the fi xture is already
created and that they can reuse it, thereby avoiding the effort of constructing
the fi xture anew
setUp
TestcaseObject
testMethod_n
TestcaseObject
testMethod_1Test
testMethod_n
TestcaseObject
testMethod_1Test
Trang 24When to Use It
We can use Lazy Setup whenever we need to create a Shared Fixture yet still want to be able to run each test by itself We can also use Lazy Setup instead
of other techniques such as Setup Decorator (page 447) and Suite Fixture
Set-up (page 441) if it is not crucial that the fi xture be torn down For example,
we could use Lazy Setup when we are using a fi xture that can be torn down by
Garbage-Collected Teardown (page 500) We might also use Lazy Setup when
we are using Distinct Generated Values (see Generated Value on page 723) for
all database keys and aren’t worried about leaving extra records lying around
after each test; Delta Assertions (page 485) make this approach possible
The major disadvantage of Lazy Setup is the fact that while it is easy to
discover that we are running the fi rst test and need to construct the fi xture,
it is diffi cult to determine that we are running the last test and the fi xture
should be destroyed Most members of the xUnit family of Test Automation
Frameworks (page 298) do not provide any way to determine this fact other
than by using a Setup Decorator for the entire test suite A few members of the xUnit family support Suite Fixture Setup (NUnit, VbUnit, and JUnit 4.0 and
newer, to name a few), which provides setUp/tearDown “bookends” for a Testcase
Class (page 373) Unfortunately, this ability won’t help us if we are writing our
tests in Ruby, Python, or PLSQL!
Some IDEs and Test Runners (page 377) automatically reload our classes every
time the test suite is run This causes the original class variable to go out of scope,
and the fi xture will be garbage-collected before the new version of the class is run
In these cases there may be no negative consequence of using Lazy Setup.
A Prebuilt Fixture (page 429) is another alternative to setting up the Shared
Fixture for each test run Its use can lead to Unrepeatable Tests (see Erratic Test) if the fi xture is corrupted by some of the tests
Implementation Notes
Because Lazy Setup makes sense only with Shared Fixtures, Lazy Setup carries all the same baggage that comes with Shared Fixtures.
Normally, Lazy Setup is used to build a Shared Fixture to be used by a single
Testcase Class The reference to the fi xture is held in a class variable Things
get a bit trickier if we want to share the fi xture across several Testcase Classes.
We could move both the Lazy Initialization logic and the class variable to a
Testcase Superclass (page 638) but only if our language supports inheritance of
class variables The other alternative is to move the logic and variables to a Test
Helper (page 643)
Lazy Setup
Trang 25Of course, we could use an approach such as reference counting as a way
to know whether all Test Methods (page 348) have run The challenge would
be to know how many Testcase Objects (page 382) are in the Test Suite
Object (page 387) so that we can compare this number with the number of times
thetearDown method has been called I have never seen anyone do this so I won’t
call it a pattern! Adding logic to the Test Runner to invoke a tearDown method at
the Test Suite Object level would amount to implementing Suite Fixture Setup.
Motivating Example
In this example, we have been building a new fi xture for each Testcase Object:
public void testGetFlightsByFromAirport_OneOutboundFlight()
Not surprisingly, these tests are slow because creating the airports and fl ights
involves a database We can try refactoring these tests to set up the fi xture in the
setUp method (Implicit Setup; see page 424):
protected void setUp() throws Exception {
facade = new FlightMgmtFacadeImpl();
helper = new FlightManagementTestHelper();
setupStandardAirportsAndFlights();
oneOutboundFlight = findOneOutboundFlight();
}
Lazy Setup
Trang 26protected void tearDown() throws Exception { removeStandardAirportsAndFlights();
}
public void testGetFlightsByOriginAirport_NoFlights_td() throws Exception {
// Fixture Setup BigDecimal outboundAirport = createTestAirport("1OF");
try { // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(outboundAirport);
// Verify Outcome assertEquals(0,flightsAtDestination1.size());
} finally { facade.removeAirport(outboundAirport);
} }
public void testGetFlightsByFromAirport_OneOutboundFlight() throws Exception {
// Exercise System List flightsAtOrigin = facade.getFlightsByOriginAirport(
oneOutboundFlight.getOriginAirportId());
// Verify Outcome assertOnly1FlightInDtoList( "Flights at origin", oneOutboundFlight, flightsAtOrigin);
}
public void testGetFlightsByFromAirport_TwoOutboundFlights() throws Exception {
FlightDto[] outboundFlights = findTwoOutboundFlightsFromOneAirport();
// Exercise System List flightsAtOrigin = facade.getFlightsByOriginAirport(
outboundFlights[0].getOriginAirportId());
// Verify Outcome assertExactly2FlightsInDtoList( "Flights at origin", outboundFlights, flightsAtOrigin);
}
This doesn’t speed up our tests one bit because the Test Automation Framework
calls the setUp and tearDown methods for each Testcase Object All we have done
is moved the code We need to fi nd a way to set up the fi xture only once per test run
Lazy Setup
Trang 27Refactoring Notes
We can reduce the number of times we set up the fi xture by converting this test to
Lazy Setup Because the fi xture setup is already handled by the setUp method, we
need simply insert the Lazy Initialization logic into the setUp method so that only
the fi rst test will cause it to be run We must not forget to remove the tearDown logic,
because it will render the Lazy Initialization logic useless if it removes the fi xture
after each Test Method has run! Sorry, but there is nowhere that we can move
this logic to so that it will be run after the last Test Method has completed if our
xUnit family member doesn’t support Suite Fixture Setup.
Example: Lazy Setup
Here is the same test refactored to use Lazy Setup:
protected void setUp() throws Exception {
protected void tearDown() throws Exception {
// Cannot delete any objects because we don't know
// whether this is the last test
}
While there is a tearDown method on AirportFixture, there is no way to know when
to call it! That’s the main consequence of using Lazy Setup Because the variables
are static, they will not go out of scope; hence the fi xture will not be garbage
col-lected until the class is unloaded or reloaded
The tests are unchanged from the Implicit Setup version:
public void testGetFlightsByFromAirport_OneOutboundFlight()
Trang 28public void testGetFlightsByFromAirport_TwoOutboundFlights() throws Exception {
FlightDto[] outboundFlights = findTwoOutboundFlightsFromOneAirport();
// Exercise System List flightsAtOrigin = facade.getFlightsByOriginAirport(
outboundFlights[0].getOriginAirportId());
// Verify Outcome assertExactly2FlightsInDtoList( "Flights at origin", outboundFlights, flightsAtOrigin);
}
Lazy Setup
Trang 29Suite Fixture Setup
How do we cause the Shared Fixture to be built before the
fi rst test method that needs it?
We build/destroy the shared fi xture in special methods called by the Test
Automation Framework before/after the fi rst/last Test Method is called.
Shared Fixtures (page 317) are commonly used to reduce the amount of per-test
overhead required to set up the fi xture Sharing a fi xture involves extra test
program-ming effort because we must create the fi xture and have a way of discovering the
fi xture in each test Regardless of how the fi xture is accessed, it must be initialized
(constructed) before it is used
Suite Fixture Setup is one way to initialize the fi xture if all the Test
Meth-ods (page 348) that need it are defi ned on the same Testcase Class (page 373)
How It Works
We implement or override a pair of methods that the Test Automation
Frame-work (page 298) calls automatically The name or annotation of these methods
varies between members of the xUnit family but all work the same way: The
framework calls the Suite Fixture Setup method before it calls the setUp method
for the fi rst Test Method; it calls the Suite Fixture Teardown method after it
calls the tearDown method for the fi nal Test Method (I would have preferred to
Inline Setup Exercise Verify Inline Teardown
Inline Setup Exercise Verify Inline Teardown
Trang 30say, “method on the fi rst/fi nal Testcase Object” but that isn’t true: NUnit, unlike
other members of the xUnit family, creates only a single Testcase Object See the
sidebar “There’s Always an Exception” on page 384 for details.)
When to Use It
We can use Suite Fixture Setup when we have a test fi xture we wish to share
between all Test Methods of a single Testcase Class and our variant of xUnit
sup-ports this feature This pattern is particularly useful if we need to tear down the
fi xture after the last test is run At the time of writing this book, only VbUnit,
NUnit, and JUnit 4.0 supported Suite Fixture Setup “out of the box.” Nevertheless,
it is not diffi cult to add this capability in most variants of xUnit
If we need to share the fi xture more widely, we must use either a Prebuilt
Fixture (page 429), a Setup Decorator (page 447), or Lazy Setup (page 435)
If we don’t want to share the actual instance of the fi xture but we do want to
share the code to set up the fi xture, we can use Implicit Setup (page 424) or
Delegated Setup (page 411)
The main reason for using a Shared Fixture, and hence Suite Fixture Setup,
is to overcome the problem of Slow Tests (page 253) caused by too many test
fi xture objects being created each time every test is run Of course, a Shared
Fixture can lead to Interacting Tests (see Erratic Test on page 228) or even
a Test Run War (see Erratic Test); the sidebar “Faster Tests Without Shared
Fixtures” (page 319) describes other ways to solve this problem
Implementation Notes
For Suite Fixture Setup to work properly, we must ensure that the fi xture is
remembered between calls to the Test Methods This criterion implies we need to
use a class variable, Registry [PEAA], or Singleton [GOF] to hold the references
to the fi xture (except in NUnit; see the sidebar “There’s Always an Exception”
on page 384) The exact implementation varies from one member of the xUnit
family to the next Here are a few highlights:
• In VbUnit, we implement the interface IFixtureFrame in the Testcase Class, thereby causing the Test Automation Framework (1) to call the IFixture Frame_Create method before the fi rst Test Method is called and (2) to call
theIFixtureFrame_Destroy method after the last Test Method is called
• In NUnit, the attributes [TestFixtureSetUp] and [TestFixtureTearDown] are used inside a test fi xture to designate the methods to be called (1) once
Suite
Fixture
Setup
Trang 31prior to executing any of the tests in the fi xture and (2) once after all
tests are completed
• In JUnit 4.0 and later, the attribute @BeforeClass is used to indicate that a
method should be run once before the fi rst Test Method is executed The
method with the attribute @AfterClass is run after the last Test Method
is run JUnit allows these methods to be inherited and overridden; the
subclass’s methods are run between the superclass’s methods
Just because we use a form of Implicit Setup to invoke the construction and
destruction of the test fi xture, it doesn’t mean that we should dump all the fi xture
setup logic into the Suite Fixture Setup We can call Creation Methods (page 415)
from the Suite Fixture Setup method to move complex construction logic into
places where it can be tested and reused more easily, such as a Testcase
Super-class (page 638) or a Test Helper (page 643)
public void testGetFlightsByOriginAirport_OneOutboundFlight(){
FlightDto expectedFlight = helper.findOneOutboundFlight();
// Exercise System
Suite Fixture Setup
Trang 32IList flightsAtOrigin = facade.GetFlightsByOriginAirport(
-Figure 20.1 The calling sequence of Implicit Setup and Test Methods The
setupStandardAirportsAndFlights method is called before each Test Method The
hori-zontal lines delineate the Test Method boundaries
Refactoring Notes
Suppose we want to refactor this example to a Shared Fixture If we don’t care
about destroying the fi xture when the test run is fi nished, we could use Lazy Setup.
Otherwise, we can convert this example to a Suite Fixture Setup strategy by simply
moving our code from the setUp and tearDown methods to the suiteFixtureSetUp and
suiteFixtureTearDown methods, respectively
In NUnit, we use the attributes [TestFixtureSetUp] and [TestFixtureTearDown] to
indicate these methods to the Test Automation Framework If we don’t want
to leave anything in our setUp/tearDown methods, we can simply change the
attributes from [Setup] and TearDown to [TestFixtureSetUp] and [TestFixtureTearDown],
respectively
Example: Suite Fixture Setup
Here’s the result of our refactoring to Suite Fixture Setup:
Trang 33public void testGetFlightsByOrigin_OneOutboundFlight() {
FlightDto expectedFlight = helper.findOneOutboundFlight();
Trang 34Figure 20.2 The calling sequence of Suite Fixture Setup and Test Methods
ThesetupStandardAndAirportsAndFlights method is called once only for the Testcase
Class rather than before each Test Method The horizontal lines delineate the
Test Method boundaries.
ThesetUp method is still called before each Test Method, along with the suite
FixtureSetUp method where we are now calling setupStandardAirportsAndFlights to
set up our fi xture So far, this is no different than Lazy Setup; the difference
arises in that removeStandardAirportsAndFlights is called after the last of our Test
Methods.
About the Name
Naming this pattern was tough because each variant of xUnit that implements
it has a different name for it Complicating matters is the fact that the Microsoft
camp uses “test fi xture” to mean more than what the Java/Pearl/Ruby/etc camp
means I landed on Suite Fixture Setup by focusing on the scope of the Shared
Fixture; it is shared across the test suite for one Testcase Class that spawns a
single Test Suite Object (page 387) The fi xture that is built for the Test Suite
Object could be called a “SuiteFixture.”
Further Reading
See http://www.vbunit.com/doc/Advanced.htm for more information on Suite
Fixture Setup as implemented in VbUnit See http://nunit.org for more
informa-tion on Suite Fixture Setup as implemented in NUnit
Suite
Fixture
Setup
Trang 35Setup Decorator
How do we cause the Shared Fixture to be built before the
fi rst test method that needs it?
We wrap the test suite with a Decorator that sets up the shared test fi xture
before running the tests and tears it down after all tests are done.
If we have chosen to use a Shared Fixture (page 317), whether for reasons of
convenience or out of necessity, and we have chosen not to use a Prebuilt
Fix-ture (page 429), we will need to ensure that the fi xFix-ture is built before each test
run Lazy Setup (page 435) is one strategy we could employ to create the test
fi xture “just in time” for the fi rst test But if it is critical to tear down the fi xture
after the last test, how do we know that all tests have been completed?
How It Works
A Setup Decorator works by “bracketing” the execution of the entire test suite
with a set of matching setUp and tearDown “bookends.” The pattern Decorator
[GOF] is just what we need to make this happen We construct a Setup
Decora-tor that holds a reference to the Test Suite Object (page 387) we wish to decorate
and then pass our Decorator to the Test Runner (page 377) When it is time to
Testcase Object
Testcase Object Fixture
SUT
TestSuiteObject
Inline Setup Exercise Verify Inline Teardown
Inline Setup Exercise Verify Inline Teardown
Inline Setup Exercise Verify Inline Teardown
Inline Setup Exercise Verify Inline Teardown
Trang 36run the test, the Test Runner calls the run method on our Setup Decorator rather
than the run method on the actual Test Suite Object The Setup Decorator
performs the fi xture setup before calling the run method on the Test Suite Object
and tears down the fi xture after it returns
When to Use It
We can use a Setup Decorator when it is critical that a Shared Fixture be set up
before every test run and that the fi xture is torn down after the run is complete
This behavior may be critical because tests are using Hard-Coded Values (see
Literal Value on page 714) that would cause the tests to fail if they are run
again without cleaning up after each run (Unrepeatable Tests; see Erratic Test on
page 228) Alternatively, this behavior may be necessary to avoid the incremental
consumption of some limited resource, such as our database slowly fi lling up
with data from repeated test runs
We might also use a Setup Decorator when the tests need to change some global
parameter before exercising the SUT and then need to change this parameter back
when they are fi nished Replacing the database with a Fake Database (see Fake
Object on page 551) in an effort to avoid Slow Tests (page 253) is one common
reason for taking this approach; setting global switches to a particular confi
gura-tion is another Setup Decorators are installed at runtime, so nothing stops us
from using several different decorators on the same test suite at different times (or
even the same time)
As an alternative to a Setup Decorator, we can use Suite Fixture Setup (page 441) if we only want to share the fi xture across the tests in a single Testcase
Class (page 373) and our member of the xUnit family supports this behavior If
it is not essential that the fi xture be torn down after every test run, we could use
Lazy Setup instead
Implementation Notes
A Setup Decorator consists of an object that sets up the fi xture, delegates test
execution to the test suite to be run, and then executes the code to tear down the
fi xture To better line up with the normal xUnit calling conventions, we typically
put the code that constructs the test fi xture into a method called setUp and the
code that tears down the fi xture into a method called tearDown Then our Setup
Decorator’srun logic consists of three lines of code:
Setup
Decorator
Trang 37There are several ways to build the Setup Decorator.
Variation: Abstract Setup Decorator
Many members of the xUnit family of Test Automation Frameworks (page 298)
provide a reusable superclass that implements a Setup Decorator This class
usually implements the setUp/run/tearDown sequence as a Template Method [GOF]
All we have to do is to subclass this class and implement the setUp and tearDown
methods as we would in a normal Testcase Class When instantiating our Setup
Decorator class, we pass the Test Suite Object we are decorating as the
construc-tor argument
Variation: Hard-Coded Setup Decorator
If we need to build our Setup Decorator from scratch, the “simplest thing that
could possibly work” is to hard-code the name of the decorated class in the
to act as the Test Suite Factory (see Test Enumeration on page 399) for the
decorated suite
Variation: Parameterized Setup Decorator
If we want to reuse the Setup Decorator for different test suites, we can
param-eterize its constructor method with the Test Suite Object to be run This means
that the setup and teardown logic can be coded within the Setup Decorator,
thereby eliminating the need for a separate Test Helper (page 643) class just to
reuse the setup logic across tests
Variation: Decorated Lazy Setup
One of the main drawbacks of using a Setup Decorator is that tests cannot
be run by themselves because they depend on the Setup Decorator to set up
the fi xture We can work around this requirement by augmenting the Setup
Decorator with Lazy Setup in the setUp method so that an undecorated Testcase
Object (page 382) can construct its own fi xture The Testcase Object can also
remember that it built its own fi xture and destroy it in the tearDown method This
functionality could be implemented on a generic Testcase Superclass (page 638)
so that it has to be built and tested just once
Setup Decorator
Trang 38The only other alternative is to use a Pushdown Decorator That would negate any test speedup the Shared Fixture bought us, however, so this approach can
be used only in those cases when we use the Setup Decorator for reasons other
than setting up a Shared Fixture.
Variation: Pushdown Decorator
One of the main drawbacks of using a Setup Decorator is that tests cannot be
run by themselves because they depend on the Setup Decorator to set up the
fi xture One way we can circumvent this obstacle is to provide a means to push
the decorator down to the level of the individual tests rather than the whole test
suite This step requires a few modifi cations to the TestSuite class to allow the
Setup Decorator to be passed down to where the individual Testcase Objects
are constructed during the Test Discovery (page 393) process As each object is
created from the Test Method (page 348), it is wrapped in the Setup Decorator
before it is added to the Test Suite Object’s collection of tests
Of course, this negates one of the major sources of the speed advantage created
by using a Setup Decorator by forcing a new test fi xture to be built for each
test See the sidebar “Faster Tests Without Shared Fixtures” on page 319 for
other ways to address the test execution speed issue
Motivating Example
In this example, we have a set of tests that use Lazy Setup to build the Shared
Fixture and Finder Methods (see Test Utility Method on page 599) to fi nd the
objects in the fi xture We have discovered that the leftover fi xture is causing
Unrepeatable Tests, so we want to clean up properly after the last test has fi
protected void tearDown() throws Exception {
// Cannot delete any objects because we don't know
// whether this is the last test
}
Setup
Decorator
Trang 39Because there is no easy way to accomplish this goal with Lazy Setup, we
must change our fi xture setup strategy One option is to use a Setup Decorator
instead.
Refactoring Notes
When creating a Setup Decorator, we can reuse the exact same fi xture setup
logic; we just need to call it at a different time Thus this refactoring consists
mostly of moving the call to the fi xture setup logic from the setUp method on the
Testcase Class to the setUp method of a Setup Decorator class Assuming we have
an Abstract Setup Decorator available to subclass, we can create our new
sub-class and provide concrete implementations of the setUp and tearDown methods
If our instance of xUnit does not support Setup Decorator directly, we can
create our own Setup Decorator superclass by building a single-purpose Setup
Decorator and then introducing a constructor parameter and instance variable
to hold the test suite to be run Finally, we do an Extract Superclass [Fowler]
refactoring to create our reusable superclass
Example: Hard-Coded Setup Decorator
In this example, we have moved all of the setup logic to the setUp method of
a Setup Decorator that inherits its basic functionality from an Abstract Setup
Decorator We have also written some fi xture teardown logic in the tearDown
method so that we clean up the fi xture after the entire test suite has been run
public class FlightManagementTestSetup extends TestSetup {
private FlightManagementTestHelper helper;
public FlightManagementTestSetup() {
// Construct the Test Suite Object to be decorated and
// pass it to our Abstract Setup Decorator superclass
Trang 40public static Test suite() {
// Return an instance of this decorator class
return new FlightManagementTestSetup();
}
}
Because this is a Hard-Coded Setup Decorator, the call to the Test Suite Factory
that builds the actual Test Suite Object is hard-coded inside the constructor The
suite method just calls the constructor
Example: Parameterized Setup Decorator
To make our Setup Decorator reusable with several different test suites, we need
to do an Introduce Parameter [JBrains] refactoring on the name of the Test Suite
Factory inside the constructor:
public class ParameterizedFlightManagementTestSetup extends TestSetup {
private FlightManagementTestHelper helper =
To make it easy for the Test Runner to create our test suite, we also need to
cre-ate a Test Suite Factory that calls the Setup Decorator’s constructor with the Test
Suite Object to be decorated:
public class DecoratedFlightManagementFacadeTestFactory {
public static Test suite() {
// Return a new Test Suite Object suitably decorated
return new ParameterizedFlightManagementTestSetup(