This article was spawned in response to a comment on Pat Shaughnessy’s awesome article Use An Ask, Dont Tell Policy With Ruby asking about TDDing the example.

Take the following text (The Lake Isle Of Innisfree)

I will arise and go now, and go to Innisfree, And a small cabin build there, of clay and wattles made: Nine bean-rows will I have there, a hive for the honeybee, And live alone in the bee-loud glade.

And I shall have some peace there, for peace comes dropping slow, Dropping from the veils of the morning to where the cricket sings; There midnight’s all a glimmer, and noon a purple glow, And evening full of the linnet’s wings.

I will arise and go now, for always night and day I hear lake water lapping with low sounds by the shore; While I stand on the roadway, or on the pavements grey, I hear it in the deep heart’s core. W. B. Yeats

We want a script to be run against this text and to match a word against it. If a match is made, that line, and all lines after it will be returned.

We already know what the code should end up looking like, we have the post. But we need to TDD it, so where do we start? To me the obvious entry point is the domain logic.

Reading a file is not domain logic. Matching a word against text and return a value is, so we will start with the lines_after test.

We will need some lines to test against, so lets load those into an array directly from the Yeats poem. We will also need our first test!

If the target word is not found anywhere in the poem, then we want to return an empty array.

In order to pass this test we need to take a single array of lines and partition it into 2 arrays, splitting it at the index of the first line to match our target word.

To accomplish this we dig into Ruby’s enumerator. The method we want is find_index, which will take a block and return the index of the first true block to be parsed. We will use a regular expression to match our word to the line.

If there are no matches find_index will return nil. So if we get a nil, we return an empty array. The other return doesn’t matter yet as we don’t have a test to describe it, so a string will work for now.

We run our tests, and we now have 1 passing test and 2 skipped ones! When your tests are green it is time to write another failing test!

For this test we want to test the reverse, what happens when there is a match found. Let’s test that if the first line of the poem matches, then all the lines of the poem will be returned.

When the tests are run this time we get an error because a string doesn’t have a count method. Let’s return the real thing instead of a test string now that we have a real test.

Since we have a match this time our index will be a positive integer. We only want the matching line and all lines after it, so we can disreguard the head of our list and return just the tail.

When the tests are run again we have 2 passing assertions, and 1 skipped one! Let’s fill out our last test to describe what happens when then match is somewhere in the middle of the list.

Run the tests again and BAM! 3 passing assertions.

And that is how you TDD this problem away functionally!