A Simple Rule of Thumb for Unit Testing
Back in my tester days, I had a rule of thumb called “none, one, some.” If there was a place in the product where I could enter a collection of things, at a minimum I wanted to see how the software would behave if I sent it nothing, one thing, or some set greater than one. This isn’t a comprehensive test set, of course, but it gave me a good feel for how that feature was working at the moment.
This week I had a chance to use that rule of thumb in my context as a developer.
I am currently working on an API that allows a customer to send us a note and, optionally, some attachments. Before writing the attachment handler, I wrote a test that asserts that the attachment count increases by one after the API call is made with appropriate credentials, a note, and one file attachment.
In Ruby-esque pseudocode, that test might look like this:
attachment = Factory.create(:attachment)
expect(thing.attach_things(attachment)).to be true
expect(thing.attachments.length).to eq 1
We are testing that the method call returns successfully, and that our attachment collection has the right number of attachments.
The test fails on the first run, of course, because the API doesn’t run. After I do the basic implementation, the test passes and everything is cool. I write another test to add two attachments and this is green on the first try, so I think we are good for at least that. I’ll definitely try this later in the UI, though. My last test is passing in no attachments. I write it, run it, and it fails—not because of missing implementation, but because of a bug I wrote.
My attachment handler takes an array of files, iterates over that array, and then sends each item off to the database. This was fine for one or more attachments. But when there are no attachments sent—not just an empty array, but nothing at all—the code fails. It turns out you can’t iterate over nil, and that is why my code was failing.
I updated my implementation to check that the attachment collection wasn’t nil before iterating over it, and the test started passing.
There was plenty more testing to do after those unit tests—more files, different file types, the various error conditions, and so on—but this test set gives me some basic confidence, and it’s useful every time I refactor or change code that is related.
“None, one, some” works for testers because of that exact pattern of failure I mentioned. Making a single thing work usually isn’t a big deal. Making a collection work might be tricky. And making the product work with nothing when there is supposed to be something is an area we often forget about.
Covering this scenario in unit tests does a favor to you and everyone who has to test that functionality. Wondering about basic functionality can be put to rest in a matter of seconds.