Tweaking automated checks - part one

27/01/2016 18:55

Originally this was to be a post about unit tests. Therefore I was hesitant to write this blog post. The testers that might read my blog probably won't need the knowledge as they usually don't write unit tests, the programmers who might read my blog probably know this already and have given the topic some thought and the programmers who could need the knowledge will probably never come across my blog. However, this post wanted out. And most things apply also to higher level checks.

In the past I have seen some cruel mutilations of automated checks. Test methods with seriously high numbers of lines of code, test methods with poor naming of anything used in them and test methods that were hopelessly cluttered. All in all, checks that were not useful. A check that doesn't tell me why it fails wastes my time. A check that doesn't tell me which parts of it are random and which are significant wastes my time. A check that has many unrelated assertions wastes my time. I write my checks once. They might never fail afterwards. But if they fail and don't speak clearly, I wasted my time of writing them. Either I do it properly or not at all. I listen to yoda.

Mostly from Frank's book, I adapted writing the assertion first. It seems like a tiny change but it has proven its usefulness to me over and over again. When I write checks myself or in a pair I notice that we often get stuck on the details in the beginning whenever we do not start with the assertion. Especially when the preconditions are involving a bit of work or if there are more than one or two.

Imagine you want to check the outcome of an API call. The data you get has a start date and a length and should be updated every evening. To check if the data is up to date, you need to parse the start date and add as many days to that date as the length of the data suggests. The result must be a time after midnight today. These are not complicated operations but it'll take a bit of time and code to write them. And when you wrote all of that you hopefully remember what you wanted.

I tackle this a bit differently to make it easier to write for me: I start with the assertion. I start with what outcome I want to finish. And I don't insist on having compiling code at all times. So the first line of code will probably look a bit like

isUpToDate(data) should be true

or ideally data should be upToDate, but I keep this little tidbit for later, read further on and you'll know what I mean.

Now I have an idea what I need: A function that figures out if data is up to date and the data itself. Both of which aren't even defined yet. So let's do that and start with the data. Let's create data with startdate yesterday and a length of one. And for the function I'd write pseudocode first, bottom-up to figure out what else I need:

def isUpToDate(data: DataObject) = {

after(endDate, midnightToday)

}

Great. More variables that do not exist yet. However, "after" is a valid time operation in scala. Let's get the definition of those two variables:

def isUpToDate(data: DataObject) = {

val midnightToday = now.withTimeAtStartOfDay

val endDate = startDate.plusDays(data.length)

after(endDate, midnightToday)

}

Thankfully, scala's nscala_time library gives us easy means of going to 0:00 of a day. The only thing left undefined is the startDate:

def isUpToDate(data: DataObject) = {

val startDate = Utils.parse(data.startDate)

val midnightToday = now.withTimeAtStartOfDay

val endDate = startDate.plusDays(data.length)

after(endDate, midnightToday)

}

There you go. By starting far down at the end, I had easy means of writing that code. You might think that you could have written this as easily from the top. And you're probably right, but if you notice getting stuck on writing a check, try out this approach. You'll end up with exactly what you need and nothing more or less while concentrating on exactly what you want to achieve. Most of the time you'll also notice easily what blocks of code ask for being in a separate method. I use this method even while not writing test code.

One disclaimer though: You can end up writing a lot of code this way before it actually compiles. Keep that in mind. This can end up in a "big bang release" of the code when you finally get to the point that it compiles. It is a risk. But one I am happy to take in many cases.

This is the first post of a small series. Continue reading about more tweaks here. Comment if you want.