Skip to main content

Phases of TDD - Learn by example

I have already written on getting started with TDD. Let me extend it further and mention in theory what are all the phases involved in a TDD.

Before jumping into this article, I hope you read the getting started with TDD article.  

There might be more steps involved in TDD. However we're just going to discuss the phases of TDD and what do we do in each phase.

/**

 * @disclaimer
 * I'm just preaching what I have learnt & practice
 * Some parts of it could be wrong
 * Please feel free to leave comments if am wrong
 * And I would be happy to stand corrected

*/

The three phases of TDD are the,

  • Red phase
  • Green phase
  • Blue phase

Red phase

This is the very first phase in TDD where we write a test for it to fail. 

Green phase

This is the second phase and immediate next phase to red phase where we write code for the test that we just wrote in the red phase to pass. 

Blue phase

This is the third and final phase of a test where you refactor the test to simplify it down further.

These phases are repeated once again for the next business use case. These definitions might look too less detail until we do it practically. 

[Image source - medium.com]

Therefore, let's take the old example,
Write a method that accepts two positive numbers as input and returns their sum


Case 1 - Function addPositive exists

Red phase

  • it('should have addPositive method'); - The test case for this would fail at this point.

To take this to the green phase, we need to write code for it. 

Therefore, writing code for it,

    function addPositive(a,b){}

Green phase

  • it('should have addPositive method'); - Passes now as we have the method written

There's no refactor required here and so we move on to the red phase of case 2.


Case 2 - Accepts two numbers as input

Red phase

  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two numbers as input'); - The test case for this would fail because it does not throw any error
  • it('should not accept non numbers as input'); - The test case for this would fail because it does not throw any error
To take these tests to the green phase, we need to update the code for it. Therefore writing down code for it,

    function addPositive(a,b){
        if(typeof a !== "number" || typeof b !== "number"){
            throw new Error("Input arguments should be of type number");
        }
    }

Here, if you notice, we have written two cases. One for success scenario and another for failure scenario. Learn more about failure scenario here.

Green phase

Now the above tests enters the green phase.
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two numbers as input'); - Passes now as we have updated the method
  • it('should not accept non numbers as input'); - Passes now as we have updated the method
Once again, there's no refactor required here in the tests. Therefore there's no blue phase and moving on to the red phase of the next case.

Case 3 - Accepts two positive numbers as input


Red phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two numbers as input'); - Passes now as we have updated the method
  • it('should not accept non numbers as input'); - Passes now as we have updated the method
  • it('should accept two positive numbers as input'); - The test case for this would fail because it does not check if the numbers are positive.
  • it('should not accept negative numbers as input'); - The test case for this would fail because it does not check if the numbers are positive.
Now that we have the red phase tests written, we shall update the code for these to enter the green phase.

    function addPositive(a,b){
        if((typeof a !== "number" || typeof b !== "number") && (a < 0 || b < 0)){
            throw new Error("Input arguments should be of positive numbers");
        }
    }

Green phase

The method is updated for the red phase tests to enter the green phase as below,
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two numbers as input'); - Passes now as we have updated the method
  • it('should not accept non numbers as input'); - Passes now as we have updated the method
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
Now that we have the codes written for tests to pass, we could see an opportunity to merge last two test cases with previous two. This is where we enter the blue phase.

Blue phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two numbers as input'); - Passes now as we have updated the method
  • it('should not accept non numbers as input'); - Passes now as we have updated the method
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
Therefore, the blue phase (or) the refactor phase is for the tests and not the code. Thus said, if we refactor the tests, we are going to see the following three tests pass and enter the green phase again,
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
Thus we move on to the red phase of the next case.

Case 4 - Returns sum of the numbers


Red phase

  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
  • it('should return sum of two numbers'); - The test case for this would fail because it does not return the sum yet.
To make the above test enter the green phase, we update the code once again,

    function addPositive(a,b){
        if((typeof a !== "number" || typeof b !== "number") && (a < 0 || b < 0)){
            throw new Error("Input arguments should be of positive numbers");
        }
        return a+b;
    }

Green phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
  • it('should return sum of two numbers'); - Passes now as we have updated the method to return sum
However once again there's an opportunity to refactor and merge the last test and second test. So they enter the blue phase.

Blue phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
  • it('should return sum of two numbers'); - Passes now as we have updated the method to return sum
Therefore if we merge these two test cases and refactor the test, we enter the final green phase.

Green phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should return sum by accepting two positive numbers as input'); - Passes now because it accepts two positive numbers as input and returns the sum as expected
  • it('should not accept negative numbers as input'); - The test case passes because our method now throws error when the input is either negative or if it is non-number.

Case 5 - Input number is greater than max value


Red phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
  • it('should return sum of two numbers'); - Passes now as we have updated the method to return sum
  • it('should throw error if input value is higher than max value'); - The test case for this would fail because in the code, we do not test the input against max value
To make the above test enter the green phase, we update the code once again,

    function addPositive(a,b){
        if((typeof a !== "number" || typeof b !== "number") && (a < 0 || b < 0) && (a == Number.MAX_VALUE || b == Number.MAX_VALUE)){
            throw new Error("Input arguments should be of positive numbers under MAX_VALUE");
        }
        return a+b;
    }

We've made code change for this failing case and now we again enter the green phase.

Green phase
  • it('should have addPositive method'); - Passes now as we have the method written
  • it('should accept two positive numbers as input'); - Passes now as we have updated the method
  • it('should not accept negative numbers as input'); - Passes now as we have updated the method
  • it('should return sum of two numbers'); - Passes now as we have updated the method to return sum
  • it('should throw error if input value is higher than max value'); - Passes now as we have updated the method to throw error if input is not under max value

Now that we have covered all cases, we can classify them as success and failure test suites as mentioned here.

Thus comes the end of the second lesson on TDD. When you follow this TDD life cycle as practice, it becomes even more easier to write code and tests in parallel as you practice.

I hope it was a good read. Do sure let me know in the comments. 

Comments

Popular posts from this blog

WebRTC - What the heck?

Over the past few weeks, I happened to work on stuff that enabled me to understand what WebRTC is and how useful it is.  The full form? Web R eal- T ime C ommunication The history It's first release was by 2011. If you want to know more, well, please read the wikipedia . WebRTC  has been a boon to web developers who want to build streaming applications or video calling applications. As you move downward, you'll just may be understand how WebRTC works but nothing technicals.  The story Let's begin with a short story. Long long ago, so long ago, nobody knew how long ago, there lived two shop keeper farmers John & Finch. It was that old point in time when barter system was a thing and money wasn't invented.  These shopkeepers lived in different cities across a river and the cities were connected by a rock solid bridge. Like how the golden gate connects the San Francisco city and the Marin County. Finch is a very private person and takes hard time to trust people. John

Git - How to drop a commit in history?

Back after a while with another git magic. We already have seen how to get rid of the top most commit in this article ->  Git magic - Make commits disappear First of all, it's one of the not so recommended way of doing it and more than that, it can only get rid of sequential commits from latest. I recently happened to get into a scenario where I had to drop a commit in the history by keeping the latest ones. As part of that exploration, presenting you this article. /**  *  @disclaimer  * Please read this post fully before executing any command. My scenario might not be same as yours. */ To my greatest surprise, I didn't know a git rebase could do this. Please continue to read to know how. The steps are simple, Count until the commit line you need to drop using git log Do an interactive rebase and you're done.  Let me explain step by step. 1. Look at the commit log & count commits This is the first step. First let's list the commits using the following command,  

TDD - How to go about it? - A quick start example

So, it's only recently I've been doing TDD. I have written unit tests before but those were all tests after writing the actual code. Confusing statement right? /**  * @disclaimer  * I'm just preaching what I practice and some parts of it could be wrong.  * Please feel free to leave comments if am wrong  * And I would be happy to stand corrected */ What is TDD? TDD, expanded as Test Driven Development could have this statement as one of the definitions. "TDD is nothing but the art of writing down the business use case one by one and write code based on it paralelly" It would feel like why do I need to write test for that? We could do the same with pen & paper and write codes. But then, you again circle back to write tests for the same. So why not do it this way?  Therefore, let's start with an example, Write a method that accepts two positive numbers as input and returns their sum Let's try and do a mock TDD for the above problem.  Case 1 - Function add