It should come as no surprise that there are some ways of designing a program that will make it easier to test using FIT. Here are some architectural motifs that I find useful to keep in mind.
The FIT book defines two types of FIT test: the business rule test and the workflow test. It also spends a good deal of time on patterns that tease business rules out of workflows.
There is a third type of test which is mentioned in chapters 32 and 35 but not otherwise highlighted: the integration test. The first two kinds of tests are the concern of all three parties: the customer, the developer and the tester. Integration tests usually only involve the developer and the tester. This is because they don't actually create any new business rules or workflow; they are concerned that already existing business rules and workflow are properly integrated at different points in the system.
Business rules are usually tested using a ColumnFixture subclass or CalculateFixture subclass. Rick Mugridge has also been experimenting with having separate tables for rules that succeed and rules that fail, thus eliminating the need for an explicit column to test for success or failure.
Business rule tests usually have two parts: configure the SUT (Software Under Test) and execute. The ColumnFixture or CalculateFixture subclass embeds any state setting and state verification needed as part of processing each rule.
Business rules can occur in any layer of the software. In general, it's good software practice to encapsulate the business rule into a specialized class that can be extracted and tested in isolation.
Workflow tests are the other major type of tests. These tests are usually done using ActionFixture or DoFixture.
Workflow tests usually have four parts: configure, create state, do something, verify state.
As a general rule, if you find yourself writing multiple passes through essentially the same workflow, each time with slight variations, you've found a business rule. It's best to abstract that rule and write one workflow test with just one of the variations.
The term Integration Test refers to tests that verify that the parts of a system work properly together. There are two forms of integration test: tests that check that all variations of a business rule are properly integrated in the workflow, and tests that check that the business rules and workflows are properly integrated at other points in the architecture.
Both types of integration test use the same FIT tests; they adapt by using different fixtures.
All large systems are built with separate, relatively independent components. Traditionally this is called a layered architecture. See Alistair Cockburn's paper on the Hexagonal (Ports and Adapters) Architecture for an alternative viewpoint that also shows where FIT slides naturally into the picture.
Regardless of whether we regard the architecture as a stack or as a cluster of cooperating components, we can usefully draw a line between where we want to test, and where the business rules exist that we want to verify. In most systems if we draw this line from the person using the system into the application domain, we will normally encounter three or four layers. The deepest layer, of course, is the domain layer. On top of that is the Application Layer, which is concerned with using domain layer resources to get something useful done. On top of the application layer is the user interface layer. This can be either one or two layers. If it's two layers, the topmost layer is concerned strictly with handling the GUI toolkit, and the lower layer is concerned with everything else, including screen to screen navigation.
The deepest Business Rule tests reside in the domain layer, while the deepest Workflow tests reside at the application layer.
One of the two kinds of integration test is to run all the variations of a business rule through the workflow(s) that use it.
This level of test is run at the same level as the basic workflow test, but it can also be run at higher levels. Unlike the workflow test that it's based on, it uses the business rule tables. This piece of magic is discussed, with examples, in chapter 35 of the FIT book.
The essence of this integration level fixture is that it does the same thing as a business rules fixture; that is it sets state, operates and checks state for every row. However, it sets the state, operates and checks the state using the routines in the workflow fixture.
To do this, the workflow test's methods have to be available in a form that can be integrated. This implies that the fixtures involved are written as a coherent system, not as simple one-off programs for each separate test.
Testing at the external interface has gotten a bad name, and for good reason. Classically, tests at this level tend to use very expensive tools, require specialized expertise, take a long time to run and are very fragile with respect to even minor changes to the screens they are testing.
Today, many of the commercial tools have dealt with the expertise and fragility issues to a greater or lesser degree. They are still expensive.
In the free software arena, you can usually get tools that deal with some combination of language, operating system and GUI toolkit. Java, for example, comes with a set of Robot classes. X-Windows and many versions of TCL come with automation tools that can be used for testing. There are even free libraries available for Microsoft Windows.
Regardless of the testing toolkit, external tests still take a long time to run, but there is no reason why they have to be run so frequently that they impact the rest of the development process.
That leaves fragility as the major issue. The solution is the same as the solution for deeper layers: abstraction. Developing an abstraction layer for each screen means that when the screen changes, the only thing that has to change is the abstraction class; all the tests that use it will continue to run without changes.
I've mentioned running a single test in several contexts, using different fixtures for each context. Before I talk about how to do this neat trick, I need to say a few words about why you want to do this.
In an acronym: DRY, or Don't Repeat Yourself. This is sometimes said as OAOO, or Once And Only Once. They mean the same thing: there should be one and only one authoritative source for any piece of information in the system. Andy Hunt and Dave Thomas (The Pragmatic Programmers) expound on this in the book of the same name, together with a number of strategies for making it happen.
Another way of putting it is that there should be one and only one place you need to change. If you need to change ten tests to reflect a change in a business rule, that's nine tests too many. That's not only extra work, but a distressingly large part of the time some of the changes will be wrong, adding even more work finding the problems and fixing them (or not finding them in time!)
Andy and Dave's solution is code generators, which are a perfectly fine solution when one has to generate code - something that software developers do all the time. It'll even work when the issue is to generate additional tests from a prototype. There are commercial products whose entire reason for being is to generate lots of tests from a prototype, and they can be very valuable for stress testing, among other tasks.
Regardless of how you do it, it's going to require some thought and planning.
As long as the test target is inside the applicaton boundary, the same test can be run with different fixtures. Doing this requires some thought and planning.
The easiest version of this approach runs business rule tests at the same level as the workflow tests that it is exercising. This scenario very naturally reuses the fixture methods created for the workflow tests. There are examples of this in chapter 32 of the Fit Book.
The farther away we get from the natural level of the workflow tests, the more detail the fixtures are going to have to absorb. Chapter 35 of the FIT book describes a scenario using the Robot classes in Java to simulate the actual user interface.
There are a number of ways to run with different fixtures. One very simple one is to use different search paths for the fixture libraries. This is a standard developer technique.
Another one is to have the main fixture (which must be a DoFixture for this to work) take a parameter which tells it what support to load for the testing scenario. This can be done using the +t parameter on the runner to set a run level symbol that the fixture can then test.
A second method is to generate the required tests, either in advance or at run time. Generating the tests in advance effectively produces a number of workflow tests, one for each business rule, with the values from the business rules plugged into the appropriate places. It could also produce a list of files suitable for the batch runner.
Generating the tests at run time is the same, but it is done inside of a custom runner.
One major advantage to this approach is that the generated tests are available for inspection, which can be a significant issue in regulated environments.
The other advantage is that the output of the generation process need not be FIT tests: they can be test scripts for other test automation tools, such as scripted commercial capture and replay GUI testing tools.