.com
Hosted by:
Unit testing expertise at your fingertips!
Home | Discuss | Lists

Standard Fixture

The book has now been published and the content of this chapter has likely changed substanstially.
Please see page 305 of xUnit Test Patterns for the latest information.
Also known as: Standard Context

What fixture strategy should we use?

The same design of test fixture is used in several tests.

Sketch Standard Fixture embedded from Standard Fixture.gif

To execute an automated test, we require a text fixture that is well understood and completely deterministic. There is an effort involved in designing a custom test fixture for each test.

A Standard Fixture is a way to reuse the same fixture design in several tests without necessarily sharing the same fixture instance.

How It Works

Standard Fixture is more about attitude then technology. It is about deciding ahead of time that we will design a Standard Fixture that can be used by several or many tests rather than mining a common fixture out of tests that were designed independently. In a sense, Standard Fixture is the result of "Big Design Up Front" of the test fixture for a whole suite of tests. We then define our tests using this common test fixture design.

The choice of Standard Fixture is independent of the choice between Fresh Fixture (page X) and Shared Fixture (page X). A Shared Fixture is by definition a Standard Fixture but not the other way round because Standard Fixture focuses on reusing the design of the fixture, not when it is built or it's visibility. Having decided to use a Standard Fixture, we still need to decide whether each test will build it's own instance of the Standard Fixture (a Fresh Fixture) or whether we will build it once as a Shared Fixture and reuse it across many tests.

When To Use It

When reviewing an early draft of this book with Series Editor Martin Fowler, he asked me "Do people actually do this?" This question exemplifies the philosophical divide of fixture design. Coming from an agile background, Martin lets each test pull a fixture into existence; if several tests happen to need the same fixture then it makes sense to factor it out into the setUp method and split the class into one Testcase Class per Fixture (page X). It doesn't even occur to him to design a Standard Fixture that all tests can use. So who does?

Standard Fixtures are something of a tradition in the testing (quality assessment) community. It is very commonplace to define a large Standard Fixture that is then used as a test bed for testing activities. It makes a lot of sense in the context of manual execution of many customer tests as it removes the need for each tester to spend a lot of time setting up the test environment for each customer test. Some test automaters also use Standard Fixture when defining their automated customer tests. This is especially true when using a Shared Fixture for obvious reasons.

In the xUnit community, use of a Standard Fixture simply for the sake of avoiding designing a Minimal Fixture (page X) for each test is considered undesirable and has been given the name General Fixture (see Obscure Test on page X). Implicit Setup (page X) used in conjunction with Testcase Class per Fixture is the most common example of Standard Fixture and also the least interesting because only a few Test Methods (page X) share the design of the fixture and they share it because they need the same design. As we make a Standard Fixture more reusable across many tests with disparate needs, it tends to get larger and more complex. This can lead to Fragile Fixture (see Fragile Test on page X) as the needs of new tests introduce changes that break existing clients of the Standard Fixture. Depending on how we go about building the Standard Fixture, we may also find ourselves entertaining a Mystery Guest (see Obscure Test) if the cause/effect of the system under test's (SUT) behavior is not easy to discern either because the fixture setup is hidden from the test or because it is not clear what characteristics of the referenced part of the Standard Fixture the test is using as preconditions.

A Standard Fixture will also take longer to build than a Minimal Fixture because there is more fixture to construct. When we are building a Fresh Fixture for each Testcase Object (page X), this can lead to Slow Tests (page X), especially if the fixture set up involves a database. (See the sidebar Unit Test Rulz (page X)
Include the sidebar 'Unit Test Rulz' on opposite page.
for an opinion on what is acceptable for a unit test.)
Therefore, we may be better off using a Minimal Fixture to avoid the extra fixture setup overhead caused by creating objects that are only needed in other tests.

Implementation Notes

As mentioned earlier, we can use a Standard Fixture as either a Fresh Fixture or a Shared Fixture and we can set it up using either Implicit Setup or Delegated Setup (page X). (Doing it with Inline Setup (page X) would be silly as we would have to copy the code to construct the Standard Fixture to every Test Method.) . When using it as a Fresh Fixture, we can define a Test Utility Method (page X) (function or procedure) that builds the Standard Fixture that we can call from each test that needs this particular design of fixture. Alternatively, we can take advantage of xUnit support for Implicit Setup by putting all the fixture construction logic in the setUp method.

When building a Standard Fixture for use as a Shared Fixture, we can employ all the fixture setup options described in Shared Fixture including SuiteFixture Setup (page X), Lazy Setup (page X) and Setup Decorator (page X).

Motivating Example

As I mentioned earlier, we are most likely to end up using a Standard Fixture because we started that way probably because of the background of one of the project participants. So it is unlikely that we would refactor to Standard Fixture when we have tests already written to use a Minimal Fixture unless it was because we were refactoring to Testcase Class per Fixture. For the sake of illustration, let's assume that we did want to get to "here" from "there". Here's an example of a building a custom Fresh Fixture for each test.

   public void testGetFlightsByFromAirport_OneOutboundFlight_c() throws Exception {
      FlightDto outboundFlight = createOneOutboundFlightDto();
      // Exercise System
      List flightsAtOrigin = facade.getFlightsByOriginAirport( outboundFlight.getOriginAirportId());
      // Verify Outcome
      assertOnly1FlightInDtoList( "Flights at origin", outboundFlight,
                                  flightsAtOrigin);
   }
  
   public void testGetFlightsByFromAirport_TwoOutboundFlights_c() throws Exception {
      FlightDto[] outboundFlights = createTwoOutboundFlightsFromOneAirport();
      // Exercise System
      List flightsAtOrigin = facade.getFlightsByOriginAirport(
                       outboundFlights[0].getOriginAirportId());
      // Verify Outcome
      assertExactly2FlightsInDtoList( "Flights at origin", outboundFlights,
                                      flightsAtOrigin);
   }
Example CustomTestFixture embedded from java/com/clrstream/ex6/services/test/FlightManagementFacadeTest.java

To keep this test short, we have used Delegated Setup to populate the SUT with the Minimal Fixture needed for each test. We could have included the fixture setup code inline in each method but that would be taking us down the road of Obscure Test.

Refactoring Notes

Technically speaking, converting a pile of tests to Standard Fixture isn't really a "refactoring" because we are changing the behavior of these tests. The biggest challenge is designing the reusable Standard Fixture in such a way that each Test Method that uses it can find some part of the fixture that serves its needs. This means synthesizing all the individual purpose-built Minimal Fixtures into a single "jack of all trades" fixture. This can be a non-trivial exercise if there are a lot of tests involved.

The easy and mechanical part of the refactoring is to covert the logic in each test that constructs the fixture into calls to Finder Methods (see Test Utility Method) that retrieve the appropriate part of the Standard Fixture. This is most easily done as a two part exercise. First, we extract the inline fixture construction logic in each Test Method into one or more Creation Methods (page X) with Intent Revealing Names[SBPP]. Then we do a global replace on the "create" part of each call to "find". Finally, we generate (either manually or using our IDE's "quick fix" capability) the Finder Methods needed to get the calls to compile. Inside each Finder Methods we add in code to return the relevant part of the Standard Fixture.

Example: Standard Fixture

Here's the same example converted to use a Standard Fixture:

   public void testGetFlightsByFromAirport_OneOutboundFlight() throws Exception {
      setupStandardAirportsAndFlights();
      FlightDto outboundFlight = findOneOutboundFlight();
      // Exercise System
      List flightsAtOrigin = facade.getFlightsByOriginAirport(
                     outboundFlight.getOriginAirportId());
      // Verify Outcome
      assertOnly1FlightInDtoList( "Flights at origin", outboundFlight,
                                  flightsAtOrigin);
   }
   
   public void testGetFlightsByFromAirport_TwoOutboundFlights() throws Exception {
      setupStandardAirportsAndFlights();
      FlightDto[] outboundFlights = findTwoOutboundFlightsFromOneAirport();
      // Exercise System
      List flightsAtOrigin = facade.getFlightsByOriginAirport(
                     outboundFlights[0].getOriginAirportId());
      // Verify Outcome
      assertExactly2FlightsInDtoList( "Flights at origin", outboundFlights,
                                      flightsAtOrigin);
   }
Example StandardTestFixture embedded from java/com/clrstream/ex6/services/test/FlightManagementFacadeTest.java

To make the use of a Standard Fixture really obvious, I have used a Fresh Fixture that is set up explicitly in each test by calling the same Creation Method to set up the Standard Fixture (Delegated Setup). We could have achieved the same effect by putting the fixture construction logic into the setUp method thus using Implicit Setup. The resulting test would look identical to one that uses a Shared Fixture.



Page generated at Wed Feb 09 16:39:41 +1100 2011

Copyright © 2003-2008 Gerard Meszaros all rights reserved

All Categories
Introductory Narratives
Web Site Instructions
Code Refactorings
Database Patterns
DfT Patterns
External Patterns
Fixture Setup Patterns
Fixture Teardown Patterns
Front Matter
Glossary
Misc
References
Result Verification Patterns
Sidebars
Terminology
Test Double Patterns
Test Organization
Test Refactorings
Test Smells
Test Strategy
Tools
Value Patterns
XUnit Basics
xUnit Members
All "Test Strategy"
Test Automation Strategy:
--Recorded Test
--Scripted Test
--Data-Driven Test
--Test Automation Framework
Test Fixture Strategy:
--Minimal Fixture
--Standard Fixture
----Standard Context
--Fresh Fixture
--Shared Fixture
SUT Interaction Strategy:
--Back Door Manipulation
--Layer Test