Test driven development (part 2)

As said in a previous post TDD’s objective is getting “clean code that works”. (Test Driven Development By Example, Kent Beck)

Your starting point is getting the code to work, breaking any rule necessary, disregarding good design and duplication. You will get those things when addressing to the “clean code” part.

In the previous post I mentioned the three ways of getting your tests to succeed. Here are some extra tips about them:

Ø  Fake it:

It’s good for your morale to see your tests succeed. This is because you know what you are doing is leading somewhere and that it works.

It also keeps your focus on the current situation, not worrying about any future steps.

Ø  Triangulation:

It’s useful when you are not definitely sure what the correct abstraction is. You set different cases in which the test should work and find the “generic” form of it later.

Ø  Obvious Implementation:

You should only apply the obvious implementation directly only if you are sure it will work. Otherwise not only the time you were trying to save will be lost, but a lot more will be spent getting rid of the mistakes or starting over.

Besides nobody likes to fail, so it is not good for your morale.

“You should test:

Conditionals

Loops

Operations

Polymorphism

But only those that you write. Unless you have reason to distrust it, don’t test code from others.”

 (Test Driven Development By Example, Kent Beck)

When testing it is usually recommended to use the “assert first” (Test Driven Development By Example, Kent Beck) approach. Once you have established what it is that you are supposed to test for by writing the assert as your first step, you know what you need to make the test pass. So you know the things you need to get there an can work your way backwards to create them and get them.

The key to TDD is to be able to control the gap between your steps. It is very important that you are capable to take really small steps when you do not know where to start from. Once you gain some confidence in what you are doing, try taking bigger steps, always having in mind that should trouble come, you ought to go back to smaller steps.

When refactoring for example, it is very important to take small steps. Do not refactor loads of code at once, instead do it one step at a time.

i.e.: If you are seeking to remove duplication, bring the elements closer slowly, and just get rid of the duplication when they do exactly the same.

You should really trust your tests. If you think just having them around is might keep you from making a mistake you should keep them. Also based on the basics of TDD your tests shouldn’t relate, meaning that the result of a test is not supposed to affect the outcome of another. When you know that you code works, you can continue developing without worrying the least bit about breaking the existing code.

Applying the three out of your four extremities should be attached at all times, the same applies to TDD. In its most pure form, you should only make changes when only a step away from getting a test to succeed.

Here are some examples of a possibly poor design:

v  “Long setup code—

If you have to spend a hundred lines creating the objects for

one simple assertion, something is wrong. Your objects are too big and need to be split.

v  Setup duplication

If you can’t easily find a common place for common setup code, there are too many objects too tightly intertwingled.

v  Long running tests

TDD tests that run a long time won’t be run often, and

often haven’t been run for a while, and probably don’t work. Worse than this,

though, they suggest that testing the bits and pieces of the  application is hard.

Difficulty testing bits and pieces is a design problem, and needs to be addressed with design. (The equivalent of 9.8 m/s2 is the ten minute test suite. Suites that take longer than 10 minutes inevitably get trimmed, or the application tuned up, so the suite takes 10 minutes again.)

v  Fragile tests

Tests that break unexpectedly suggest that one part of the application

is surprisingly effecting another part. You need to design until the effect

at a distance is eliminated, either by breaking the connection or by bringing the two parts together.”

(Test Driven Development By Example, Kent Beck)

When using TDD there is a very important difference between doing it alone or as part of a team.

If you do it on your own you should leave the last test broken. This way the next time you get to your code you must pick up the thread and get a hold of what you where trying to do much faster.

Else, if you are a member of a team, you must leave all your tests running. This shows responsibility between teammates but more important, is that you don’t know what happened since you last worked.

Personal Thoughts:

I think TDD is not for everyone. It is a technique that requires patience, hard work, repetition and most important of all constant testing.

Tests are the center stone of TDD. You are basically not supposed write a code implementation without having the test to backup it up first.

If you are willing to apply this technique, you must know that it might be reiterative and your code won’t flow as fluently as it used to, at least the first times.

In my specific case, I think the use of this process will benefit me, since it will allow more confidence while developing, although it will be difficult getting used to test for every implementation.

Here’s a table of Pros and Cons about TDD as I see it.

Pros

Cons

Your tests backup your code. This gives you confidence in it.

As much code is written for tests as it is in the functional code.

If you need to change your code you can do it, since your tests will not allow you to break the previous working code.

Not everyone is capable of taking small steps. It takes time, practice and will to get this done correctly.

If a test doesn’t work, it doesn’t affect the others.

If a test is not working, you are not supposed to continue until it is fixed. This could get you in a tough spot.

If you have a project seemingly big for you, allows to divide into smaller instances, setting shorter milestones to accomplish.

Testing first saves time, because it avoids code refactoring due to mistakes done for not having tests.