7
Design for Testability
Automated testing is much simpler if we adopt a Layered Architecture [DDD,
PEAA, WWW]. At a minimum, we should separate our business logic from the
database and the user interface, thereby enabling us to test it easily using either
Subcutaneous Tests or Service Layer Tests (see Layer Test). We can minimize any
dependence on a Database Sandbox (page 650) by doing most—if not all—of
our testing using in-memory objects only. This scheme lets the runtime environ-
ment implement Garbage-Collected Teardown (page 500) for us automatically,
meaning that we can avoid writing potentially complex, error-prone teardown
logic (a sure source of Resource Leakage; see Erratic Test). It also helps us avoid
Slow Tests (page 253) by reducing disk I/O, which is much slower than memory
manipulation.
If we are building a GUI, we should try to keep the complex GUI logic out
of the visual classes. Using a Humble Dialog (see Humble Object on page 695)
that delegates all decision making to nonvisual classes allows us to write unit
tests for the GUI logic (e.g., enabling/disabling buttons) without having to
instantiate the graphical objects or the framework on which they depend.
If the application is complex enough or if we are expected to build compo-
nents that will be reused by other projects, we can augment the unit tests with
component tests that verify the behavior of each component in isolation. We
will probably need to use Test Doubles to replace any components on which
our component depends. To install the Test Doubles at runtime, we can use
either Dependency Injection (page 678), Dependency Lookup (page 686), or a
Subclassed Singleton (see Test-Specifi c Subclass on page 579).
Test Organization
If we end up with too many Test Methods on our Testcase Class, we can con-
sider splitting the class based on either the methods (or features) verifi ed by the
tests or their fi xture needs. These patterns are called Testcase Class per Fea-
ture (page 624) and Testcase Class per Fixture (page 631), respectively. Testcase
Class per Fixture allows us to move all of the fi xture setup code into the setUp
method, an approach called Implicit Setup (page 424). We can then aggregate
the Test Suite Objects (page 387) for the resulting Testcase Classes into a single
Test Suite Object, resulting in a Suite of Suites (see Test Suite Object) containing
all the tests from the original Testcase Class. This Test Suite Object can, in turn,
be added to the Test Suite Object for the containing package or namespace. We
can then run all of the tests or just a subset that is relevant to the area of the
software in which we are working.
The Simplest Test Automation Strategy That Could Possibly Work