Exploration Through ExampleExample-driven development, Agile testing, context-driven testing, Agile programming, Ruby, and other things of interest to Brian Marick
|
Thu, 01 Jan 2004Tests as documentation for later readers: an example I think it's hard for people to understand what I mean when I talk about product-level tests driving coding. It's easy to think that the actual executable tests are the only thing the programmer uses to understand the problem to be solved. That's not true. There's conversation with other people. And there are annotations attached to the tests. Thinking about what it would mean for tests alone to drive coding gives little or no understanding of what I really mean. Conversation is hard to show in a blog, but I can show what I mean by annotations. What follows are the actual tests I used to add a new type of fixture to FIT (a table-driven testing framework). Twelve people will soon be using this fixture, and I wanted these tests also to help them understand how to use it. But what I mainly used them for was guiding me, step-by-step, through the coding. The example is as I wrote it, except that I removed the egregious glop that Word insists on putting in HTML files. Looking back on it from a few days' remove, it has obvious weaknesses when you approach it expecting user documentation. But I think flaws are typical, so I will not fix them just to save myself embarrassment. It's likely the tests will be confusing to people who don't know FIT. One of the characteristics of Agile documentation, I think, is that it does not attempt to educate the reader into the tacit and explicit knowledge that can be had in other ways. In this case, the other way is experience with FIT (which, I think, usually means reading the FIT code to answer questions). More commonly, the knowledge is had by being part of the development team. This is an odd case, because what follows uses a tool to test an addition to itself. The Java code that lies behind the tables may help, so that's included afterwards. NOTE: If you're attending the MFA for software trial run - this means you, Chad - don't read these tests. I need fresh reactions for my first lecture.
These tests are about the StepFixture
A StepFixture alone is not useful. Instead, it's subclassed. We'll use StepFixtureTestFixture.
Steps are designated by the first cell of a column. Arguments are in following cells. check is a special name: it calls the no-argument method in the second column and compares the result to the value in the third column.
State is maintained across tables, but you can reset it.
Because StudlyCaps style is not fit for humans, check arguments and other steps can be written in a more friendly style.
Arguments can be any type that FIT knows how to convert.
You shouldn't use two methods with the same name and same number of arguments. The results are undefined, but doubtless not good. A check step can also take extra arguments. The last cell remains the expected value, but cells before it are passed to the method.
package fit; import java.util.Date; import java.util.GregorianCalendar; import java.util.Calendar; public class StepFixtureTestFixture extends StepFixture { private String result = ""; private int count = 0; private String countPrefix() { count++; return count+":"; } private String delimited(String s) { return "/"+s+"/"; } public void add() { result += countPrefix() + delimited(""); } public void add(String s) { result += countPrefix() + delimited(s); } public void add(String s, String s2) { result += countPrefix() + delimited(s) + "+" + delimited(s2); } public void addAString(String s) { add(s); } public String currentString() { return result; } public boolean stringIsEmpty() { return currentString().equals(""); } private Date someRandomDate; private int someRandomInteger; public Date date() { return someRandomDate; } public int date(String day) { GregorianCalendar cal = new GregorianCalendar(); cal.setTime(someRandomDate); return cal.get(Calendar.DAY_OF_MONTH); } public int integer() { return someRandomInteger; } public int plus(int first, int second) { return first+second; } public void setADate(Date date) { someRandomDate = date; } public void setAnInteger(int integer) { someRandomInteger = integer; } } |
|