Friday 14 February 2014

Fake it till you make it


tldr; fake it till you make it is an underused approach to TDD, but when you can get it to work it gives you the best experience. See Kent Becks book TDD:By Example for a more thorough explanation (this book is highly recommended if you want to understand TDD).

Green bar patterns

In Kent Becks book, TDD: By Example he outlines three approaches to get the tests passing. There's Obvious Implementation, where you just write to code to pass the test. This method works well, but where the implementation is anything more than trivial, it can be difficult to write the correct code. Another is Triangulation, where you create multiple tests to force yourself to write to the implementation. Triangulation is the least recommended by Kent Beck of all patterns because you end up with a lot of redundant tests. Unfortunately, it is probably the most used.

There's a third approach though which I find gives me the best results (when I can get it right) and that's called Fake It (Till You Make It). The idea with Fake It is to hard code the expected result to get to green straight away. Then remove duplication between the test and the implementation.

Using the word wrap example.

I'm going to work through this with the word wrap kata, which is:
Break a piece of text into lines, but with line breaks inserted at just the right places to make sure that no line is longer than the column number. You try to break lines at word boundaries.
First test, should not wrap text that fits.
def test_should_not_wrap_text_that_fits(self):
    self.assertEqual(wrap("text that fits", 20), "text that fits")
and the faked result:
def wrap(text, column):
    return "text that fits"
The test passes. Next step is to remove any duplication between the test and the implementation. Returning the result that the test is expecting is duplication and that can be removed by returning the passed in text:
def wrap(text, column):
    return text
Test passes and there's no more duplication between the test and the implementation.

Doing this wouldn't make a whole lot of sense in the real world. Passing this test is so simple that it would be best to use Obvious Implementation, but I think it serves as a nice introduction to the technique.

Second Test, wrapping text that does not fit on a line.
def test_wrapping_text_that_does_not_fit_on_a_line(self):
    self.assertEqual(wrap("text that does not fit", 15), "text that does\n not fit")


Faking It

It is possible to pass both tests by branching and faking the new result.
def wrap(text, column):
    if len(text) > column:
        return "text that does\n not fit"
    return text
Running the tests should give two passing tests. Now to start removing the duplication. I'm going to do it as slowly as possible to show how the technique can work.

Side note, speeding up and slowing down

The steps in Fake It may seem painfully slow for some, but this is a good thing.  Fake it allows me to do go slowly or quickly, depending on my confidence. At it's slowest I'm making the simplest possible change and running the tests to ensure that I'm at green. I can speed up by taking a few of those steps together. Full speed, where I just make the change I want, is Obvious Implementation. That's the big advantage that this approach has over Obvious Implementation and Triangulation, both of which force me to write all the code at once.


Removing duplication

That returned text is made up of three separate parts, two lines and a line break. After each step here the tests are run to ensure they are still green:
def wrap(text, column):
    if len(text) > column:
        return return "text that will " + "\n" + "not fit"
    return text
That first part is really just as much as will fit in the first column.
def wrap(text, column):
    if len(text) > column:
        return text[:column] + "\n" + "not fit"
    return text
And the second part is the remainder.
def wrap(text, column):
    if len(text) > column:
        return text[:column] + "\n" + text[column:]
    return text
Most of the duplication between the test and the implementation has been removed, but there is still some. That is, it will only handle one line break. It will be easier to remove this duplication if the duplicate return statements are refactored into one.

The first step is the make the return statements the same.
def wrap(text, column):
    wrapped = ''
    if len(text) > column:
        wrapped = text[:column] + "\n"
        text = text[column:]
        return wrapped + text
    return wrapped + text
Once they're the same, the redundant return can be removed.
def wrap(text, column):
    wrapped = ''
    if len(text) > column:
        wrapped = text[:column] + "\n"
        text = text[column:]
    return wrapped + text
By doing this I've changed the structure of the code without changing the behaviour. The solution still needs to be generalised to handle any amount of lines. That's got me thinking that my current test is not good enough. I want to take the approach of changing that if to a while, but I'd like to have some safety in the move.

Triangulation can be useful

This is a situation where triangulation can be useful. I'm going to introduce one more test to ensure that all is good. The test will be wrapping text over multiple lines.
def wrapping_text_over_multiple_lines(self):
    self.assertEqual(wrap("wrapping text over multiple lines", 9), "wrapping \ntext over\n multiple\n lines")
I can pass all three tests now by changing the if to a while and appending to the wrapped text.
def wrap(text, column):
    wrapped = ''
    while len(text) > column:
        wrapped += text[:column] + "\n"
        text = text[column:]
    return wrapped + text


Not done yet

There's one final step I have to take if I want to tidy everything up. When I added that extra test at the end, I also added duplication between the tests because wrapping_text_over_multiple_lines tests the same behaviour as wrapping_text_that_does_not_fit_on_a_line. I can prove this by changing anything in the implementation to break wrapping_text_that_does_not_fit_on_a_line. This will in turn break wrapping_text_over_multiple_lines every time. The same is not true in reverse, so wrapping_text_over_multiple_lines is the test that I want to keep.

Disclaimer

This is not a perfect implementation of word wrap, the intention is not create create a perfect word wrap, just to show how fake it works.
Fake it works really well when you can get it to work, but the biggest difficulty I find is seeing the simplest change to make and sometimes even how or what to fake it. When I'm stuck I end up taking big steps or triangulating to help me move. Often, doing either of these steps will help me understand what I should have done, so I can quickly revert and go back to faking it.

Wednesday 5 February 2014

TDD: What should I test next?

tldr; focusing on the problem domain rather than the implementation will help you out

One of the guiding ideas in TDD is to implement the simplest thing that will work. This is good advice, and there is no shortage of posts explaining what it means and why you should do it. But, there's a similar question that occurs before this that always stumps me and that is 'What should I test next?'. The easy answer is the simplest behaviour that the software should have. Always testing the simplest behaviour will allow you to incrementally drive the design. Not testing the simplest behaviour tends to require big leaps in the implementation and makes it easy to get stuck a few tests in.

Initially my thought on this was that the simplest thing was the most basic arguments that I could pass in. It sounds plausible and it also got me moving quickly. The problem was that I normally got stuck after a few tests because I wasn't really analysing the problem.

It's easiest to show this with an example. I'm going to use a TDD kata called word wrap. This is the requirement:

    Break a piece of text into lines, but with line breaks inserted at just the right places to make sure that no line is longer than the column number. You try to break lines at word boundaries.

Taking the approach of using the simplest arguments would go something like this. To keep this short I'll just focus on the string argument.

First test: an empty string, column 10
Second test: a single character, column 10
third test: two characters, column 10

...and on and on. There's probably a lot of different paths you could go with this type of testing but really I was being led by the arguments rather than by the requirement. Taking it back to analysis helped me out. Initially this can be a little harder, but, you're less likely stuck because the problem domain is more likely to lead you in the right direction than the arguments for a method.

So what's, the simplest behaviour that word wrap can have? I can also ask this question in another way, what's the least amount of behaviour that I can add, while still doing something useful? The usual answer to this question for the first test is the 'do nothing' behaviour. In the case of word wrap I would think that this is a when it doesn't need to wrap at all. My specification and test name for this will be 'should not wrap a line that fits'.

The implementation that satisfies this test would look something like this:

public string Wrap(string text, int column)
{
    return text;
}

The difference between this and the previous approach is that I have one test for all lines that fit. Creating multiple tests for lines that would fit would have been pointless because I would have been re-testing the same behaviour. This is also an example of the kind of feedback that you get from your code. Not having to implement any new behaviour for a test is the code saying that you don't need that test.

Disclaimer
In this post I'm advocating the minimum most suitable tests to drive out the design. Tests should also build your confidence though, so if your instinct is telling you to add the extra tests, then go ahead and add them.