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

Confluence: 5 quick things that you need

As part of my work experiments, this week I would like to write down the things that one needs to know in confluence that can up-skill their documentation works. I will cover the following 5 things, How to Anchor link a title? How to Anchor link to a section? How to create a dashing dashboard? Panel - Confluence Macro Layouts - Confluence Tools Content by Label - Confluence Macro 1. How to Anchor link a title? This is the most required thing. Most useful when one has to refer to a section internally on the same confluence page. Let's consider you have a page with three different sections and titles as shown below, In this, if you want to add an internal anchor from a text in paragraph 3 to a title in paragraph 1, you can add it as follows, Choose the word that needs Anchor Click on the link icon from the Toolbar above In the link box, enter #Page Title 1 Click Insert That is it. Your anchor from the selected text to Page Title 1 is ready. This can be tested out in the preview itsel

Git magic - Squash commits

Back with another git magic. When it comes to merging a pull request on Github, there are two options Rebase and Merge Squash and Merge What is Rebase and Merge? When one chooses Rebase and Merge, all the commits in the PR are added to the target branch. For example, if the PR has 5 commits, all of those commits will be visible in the PR history of the target branch. What is Squash and Merge? When a PR is merged by choosing Squash and Merge, all the commits in the PR are combined into one PR and then added to the target branch. Once again, if the PR has 5 commits or any number of commits, they are combined and added to the target branch. Therefore, this is what Squash means. Combining 'n' different commits into one single commit is called squashing. In this blog post, we will go through the commands that can squash commits.  Advantages of Squashing commits No more redundant commits In a pull request, one may have 'n' different commits for one change. They might have bee

npm-link | What NPM won't tell you!

Hello readers. So back with another easy yet unexplored feature of npm/yarn packages. We as frontend developers / SDK developers, have to deal with more than one repositories where we actually code contribute. Most SDK developers would know this already or they use the not-so-well documented 'npm link' command . /**  *  @disclaimer  * Please read this post fully before executing any command. My scenario might not be the same as yours.  * This article below uses a repo from my current workplace as an example which is open-source and does not violate the Cisco Confidentiality Policies */ To make this article easier to understand, some representations, Host - Package that needs another local package as part of its node modules. Let's assume the path to this package is  ~/Documents/Repos/demo-app Adhoc - Local package that is added into another package as a dependency. Let's assume the path to this package is  ~/Documents/Repos/semver-monorepo What is npm link? This is a co