Quang Hoàng
Quang Hoàng

Reputation: 359

How to apply TDD with complicated function?

Uncle Bob's three rules of test-driven development state the following:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.

  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.

  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

In real world, for example, I have a task to complete a method which requires some complex algorithms (some advanced math knowledge for example), if I apply 3 above rules, it will be like this:

My question is: is this approach quite unrealistic and distracted in such situations?

Why don't we write the first test case, then focus on finding the solution then implement it, I mean looking at the big picture, instead of writing just enough code to pass the first test case?

Upvotes: 1

Views: 497

Answers (2)

GhostCat
GhostCat

Reputation: 140417

I think the other answer is excellent, but beyond that: if one method will require you to implement several algorithms - are you sure that such a method respects the single responsibility principle? It rather sounds like this one method is doing way too many things.

So, when you step back and see that 3 algorithms are used - that already tells us that each algorithm should probably go into its own method. And that our "initial" method would only be calling these other methods to get some computation done.

And going in some other direction - there is no law that prevents you to adapt TDD to your needs. That leads to a practice that I call "bottom up TDD".

It goes like this: instead of first writing a test for my one huge method - I actually think about the different parts I will need within that huge method. So I write down only tests for the first part; and then implement that. I do that for all parts. Over time, the parts are enhanced, and maybe I merge smaller parts into something bigger on the way (and that could well mean to merge multiple tests into a bigger one as well).

This technique can mean that you might end up with a single testcase for your huge method - but you actually used TDD to test the small parts while building "the big solution".

In other words: instead of writing one big functional test for the public contract of that one method - I start by writing tests for the small helper methods I know I will need. In the end those helpers will be private methods - to there is no point in testing them directly. But you can for keep those parts of the earlier tests that make sense in the context of your big public method.

Long story short: all of these techniques are meant to guide you finding your own way. Given that you have enough experience to design on the fly it is possible (and actually a lot of fun) to use TDD this way!

Upvotes: 3

rafaelim
rafaelim

Reputation: 683

In my experience with TDD, doing this baby steps has some advantages:

Make developers think how the code should be used

A method name that is understandable, inputs (will I read from a file, array), outputs (will be a json, ArrayList) and the behaviour for each specific input (raise exception, do nothing).

Test feedback is more precise

Doing baby steps helps you focus on how to treat specific cases in your algorithm. For example, suppose a TDD with quicksort algorithm:

def "Given an empty list, when I use quicksort, should raise an exception"()
def "Given an list of one elements, should return the list itself"()
def "Given an ordered list of elements, should return the list itself"()
def "Given an unordered list of elements, should return a copy of ordered list"()

Note that the test itself should be very precise and detailed on behaviour you want to test. A not so good test could be something like this

def "Given an list, should give me an ordered list"()

If this test above fails, it failed because the exception was not thrown? Or the list is ordered but altered the original list?

Your test code becomes the specification

If you do some wide testing code of your algorithm, you possibly will hide some important detail of your algorithm. All the important piece of your code must have a very well written test case for it. Doing so, you will end up with a test code that basically is the specification of your algorithm. If some other developers asks you how your algorithm should work, just show him the test cases.

I had the same thought as you in the beginning. It feels like we are wasting time and seems ridiculous doing these baby steps. But trust me, the more you practice, the more you realize the benefits of TDD.

Upvotes: 2

Related Questions