TDD: Behavior Testing not Bug Testing

Jason Heine on November 25, 2008 in Test Driven Development

First and foremost, what is TDD? All TDD really is, is the ability to code your application so that you can test any behavior changes with code changes in the future.

 

!!Screech!!

 

STOP! Did I just say BEHAVIOR changes? I sure did. TDD is not about finding bugs, like most people think about, it is about testing the behavior of your application and making sure that if you make a change to your application the behavior of your application does not break.

 

So does this mean we are really talking about Behavior Driven Development (BDD)? No! This has nothing to do with BDD. That is a whole different topic.

 

What does this mean?

 

Let’s move into an example:

 

Bob the wonder programmer is making a brand spanking new calculator! This calculator is top notch and above the trend with functionality. His calculator will be able to add two numbers together! Isn’t that just amazing?

 

Anyway, Bob goes out and creates a method like this:

 

        public int Add(int x, int y)

        {

            return x + y;

        }

 

This method is super simple if you ask me. Okay, so what is really happening here? You would say something like “Duh, it is adding x and y and returning the result.” Well, you are correct. This is exactly what it is doing. This is the designed behavior of this method. So what happens if we change the method to the following?

 

        public int Add(int x, int y)

        {

            return x + y + x;

        }

 

Is this a bug or a behavior change? It all depends on the person you are asking. If the intent of the change is behavior then it is a behavior change. If the intent was not intended, then it is a bug. However, because the behavior of the method changed, we need to know somehow so our application breaks.

 

Now, all things considers, yes, this is a bug. Don’t get me wrong, a bug is a bug and this problem will be reported as a bug. The point of all of this is to understand that you need to look for behavior changes when you are using TDD.

 

So how are we going to write a test which will allow us to test the behavior of our methods?

 

Using your favorite testing framework (I am using NUnit) you can begin like this:

 

using NUnit.Framework;

namespace Tester

{

    [TestFixture]

    public class TestClass

    {

        [Test]

        public void AddOnePlusOneAndGetTwo()

        {

           

        }

    }

}

 

Looking at this I have the basic shell for my test. What I am going to do next is simple. I am going to write my test for a new method (“Add”) and using Resharper (get it if you don’t have it) I will create my methods and execute my tests.

 

I now have my shell of a test, but my method does not exist yet:

 

  [Test]

        public void AddOnePlusOneAndGetTwo()

        {

            int x = 1;

            int y = 1;

            int z = Add(x, y);

            Assert.That(z == 2);

        }

 

Part of TDD, is to write your test first and then create your methods after the fact. Why do we do this? Because we need to understand what our application is going to do before

 

Okay, using my awesome resharper tool, I created my method (which does reside in the same class as my test, for now – you can move your methods and classes out later, this is part of the refactor process).

 

        private int Add(int x, int y)

        {

            throw new System.NotImplementedException();

        }

 

~ Off Topic ~

 

One of the important factors about Test Driven Development is the following:

 

RED

GREEN

REFACTOR

 

What does this mean actually? Red means you write your test to fail. Fail?? Why in the world would you want your test to fail?

 

You want your test to fail so you know you are testing the right method. What happens if you write a test and you call a method and you are not 100% sure you are calling the right method. This can happen when dealing with overloads.

 

Green means make your test pass. Simple enough.

 

Refactor is just what it means. Refactor your code into a usable design pattern that fits your application. Repeat as necessary. As you refactor your code you may not always get the red again. This is a good thing. If you do get red, something bad happened and you need to fix it.

 

~ Back on Topic ~

 

So, we are going to go ahead and run our test for the first time with our method that we created.

 

We get the wonderful error:

 

System.NotImplementedException: The method or operation is not implemented.

 

This is what we expected. Our test failed. Now, we are going to make our test pass.

 

To do this, we can do one of two things. We can write functional logic or we can write a hard code value as the return type. Granted, a hard coded value will make your test pass and you can refactor that later. However, I personally think it is a waste of precious coding efforts just to make your test pass. Take the time to write your logic to make your test pass. You will feel a bit more warm and fuzzy inside knowing that you are getting closer to the finished product.

 

Our functional method:

 

        private int Add(int x, int y)

        {

            return x + y;

        }

 

Excellent, we have a functional and logical method. Now when we run our test we get the following:

 

Success

 

Okay, as simple as it may sound we are testing the behavior our method. The behavior is the fact we are adding two numbers together.

 

So, what happens if the behavior of our application needs to change? Some vortex of quantum physics comes about and changes the thought processes of all our brains and says that when we add we really need to subtract!

 

Well, that sure would be a behavior change now wouldn’t it? (You can see where this is going can’t you?)

 

So, I go into my method and I change my + to a -:

 

        private int Add(int x, int y)

        {

            return x – y;

        }

 

Now, if I did not have any tests the application would still run. It would still call the method and it will do its calculations and return a number. We would not receive an error. The method may be return the right result (in our strange world) but since the behavior changed, we don’t know about it.

 

We can find out by running our test:

 

NUnit.Framework.AssertionException:   Expected: True
  But was:  False

 

Oops! Our test failed! This could mean one of two things. Our test is no longer valid, or our code is wrong. Since we are changing the behavior in our design, the test is no longer valid. We can both delete the test and write a new one, or we can modify the test to pass. Typically if we are going to change the behavior of our application we are going to modify the test. If the test was really expecting us to add (in the real world), yes we would have a “bug” and we would need to go fix the code.

 

Summary

Test Driven Development is used to determine the behavior of your application is working the way it should be working. TDD is (by concept) used for looking for bugs (which is really just a change in how the behavior works).

 

If you can wrap your head around the fact that you are not testing for bugs you will become an even better programmer when working with any type of application.

 

 

 

 

2 Responses to “TDD: Behavior Testing not Bug Testing”

  1. David Yancey says:

    Very nice introduction to TDD.

  2. Excellent article. I will agree that often it’s hard to emphasize to folks that TDD is about managing change and less about QA. People tend to get hung up on unit testing as another round of bug-snooping, but that completely misses the point of why we do it.

    I don’t think we’re even ready to talk about BDD yet. XD

Leave a Reply