Making Fixtures


Fixtures are objects that connect test data to the application under test. Java's object mechanisms just happen to do we need for this purpose, which is convenient. But remember, our purpose makeing fixtures is to test, not to satisfy someone else's notion of OOAD. Here is what has worked for us. -- WardCunningham

Get Ready

  • Let your customer guide you by providing some sample data illustrating aspects of the story judged important for the iteration. Try to cast this data into the form of row, column or action tables (see FieldGuideToFixtures). If this is hard, try breaking it into a combination of tables (see TimeClockExample).

  • Choose a point of contact to the system. Consider where the story will be implemented. How close to that code can your fixture get and still interact with all of it? Is there an existing interface that the code can extend or will you have to make a new one? Don't be afraid to change code to be testable: being testable is one if its responsibilities.

Grow From Tables

  • Select a simple but representitive table. Run it with no fixturing at all. See what breaks and work on that. The first thing that will break is that the named fixture won't be found and the whole table will be ignored. Its a start.

  • Create a dummy fixture that returns wrong answers. Run this before you've even written every dummy method. With each new variable or method consider carefully its granularity and type. Adjust the tables or your fixture until they match and seem powerful. Compare the following. One method relies on Permissions to parse, print and compare properly, and will make a higher level interface because of it. The other method relies only on int. (Permissions is presumably a ValueObject that is or should be part of the application.)

	public int userReadPermissionLevel() {
		// return read permission level
		return 0;
	}
	public Permissions userPermissions() {
		// return read, append and update permissions
		return new Permission(0, 0, 0);
	}

  • Call real code from your fixture a little bit at a time. Run the test frequently to see what breaks and how. How does the application work when incompletely or improperly initialized? You may be surprised.

  • Work from the more interesting to the less interesting functionality. You may reach a point where the remaining test data is gratuitous and not worth testing. Remove it from the tables. People will be relieved to stop typing it. If you need to have something to make the system work, try defaulting values with initializers that could be columns if anyone ever cared.

	String userName = "John Doe";
	Date dateOfBirth = DateFormat.getDateInstance().parse("Jan 1, 1970");

  • Add back doors to get to hard to reach places. If the method computePartialMatrix really has to be private, add a method testComputePartialMatrix that provides access with clearly stated purpose. It is possible to make a fixture an inner class. This will provide the fixture extremely intimate access to the host object; access we have considered exploiting but haven't yet found the need.

Maintain As Usual

  • Fixtures become an integral part of the application code base. Maintian them as such. Should you refactor an interface, change all the callers including the fixtures.

  • Consider writing unit tests for fixtures, especially if they are tricky (which should be avoided). You can use the acceptance test as a substitute for a unit test so long as you run the acceptance test at each refactoring, checkin and build. (See JunitBridge.)

  • Control the accumulation of tests. Its ok to add cases to a test that is already in production, say when a bug is found and fixed. But consider how these tests might duplicate testing already present. Maybe some of that can now be omitted. (See LessonsLearned #?)

  • Write new tests for new iterations in new documents. It may be that a new story calls for variations on an existing story and that this functionality can be easily tested using the old story's test and fixtures. Reuse the fixtures but not the test. Make a new document that talks about the new stuff and keep it with tests for this iteration. (Note: sometimes a story is really a bug fix that is hard enough to be scheduled like new functionality. In this case move the old test to the new iterations.)

  • Refactor tests as layers accumulate. Discard tests and unneeded fixtures when their functionality is fully tested by newer, higher level tests. It may be necessary to add a few tests to the newer tests to ensure the same (or appropriate) level of specificity. Combine tests from different iterations when they really represent the sequential development of a single concept or function.


See also HowToStepByStep.

 

Last edited October 27, 2002
Return to WelcomeVisitors