The other day, we had the task of excluding some projects from a report our system creates. The projects belonged to a category that we could easily identify. We set out to build a test, so we could start coding.
How do we test a report? We can look at it, and see if it did what we wanted, but we were looking for a unit test that we could write to do this. This is where we remembered that we could mock the report. We set about looking at what information we could get from it.
All we came up with was counting the number of lines, and making sure it was the same as the number of projects not in Category A. I’m pretty sure that was lame. The way I described it to my pair was this:
If someone comes and does something to exclude a different category (B), and that breaks our code somehow, our test could still pass, because there were the same number of excluded lines from Category B as from Category A. Or a page break enforcement could change the number of lines.
One thing I’m thinking of is that we can produce red then green, but still not have learned much. And I’m aware that from an empirical perspective, passing tests really don’t tell us anything. It’s the failing tests that tell us stuff. Passing tests are just indicators of an absence of failing tests.
What I want to know is this: how do we write strong tests? How do we focus in on the real essence, and write a test that’s tight enough to be really meaningful?
Your insight is correct. The right number of passing tests is always a judgment call, and in the beginning, that judgment is still pretty weak. It’s also easy to “overcode”, that is, write more code than your tests call for. It’s why TDD is such a tricky sport to master.
Anyway, with a report, I wonder if what’s needed is a more micro approach. (I call them microtests , actually.)
If I’m creating a report, there are several things I might want to demonstrate. Since I restrict a single object to having a single responsibility, I suspect my Report class will be what I call a host class. All it does is hook up a few other classes and let them run. For me, that’s liable to be untested. After all, declaring a couple of instances and cross-connecting them is pretty hard to break. Remember that TDD is about testing things you think can break.
My feeling is that I just don’t know enough about the rules you wanted in the report. Does it have to be paginated? Does it need to deal with 0 line-items? many? different types? Or is the important thing that it loads its incoming fields and calculates derived fields? Does it sort? and so on and so forth. Give me some more detail and we can sketch how the TDD might really go.
I constantly start one test, e.g. ReportTest, only to immediately decide that its too much, and I need something much much smaller to get started. Thank God for stacks: I push the starting test onto the stack and go after the next one, and so on, until I find something I’m sure I can do.