TDD stands for Test Driven Development. To those who don’t know what TDD means: In a typical TDD Environment, a developer start with a basic test case describing the minimal requirement for implementing the module. Then he writes the actual implementation code for making the test case pass.
Next, another test case is written for a different expectation for the module, followed by writing the implementation to make the test pass. This process goes on till the all the expectations for the actual module is implemented.
This way of development ( driven by series of test cases ) is called TDD.
Setting up the tools
Here we’re going to use Mocha as the unit testing framework. For running the tests, we will be using Testem.
We’ll be using BDD ( Behaviour Driven Development, which is similar to TDD, but instead of assertions we use expectaions) style syntax. For that purpose let’s use the excellent Chai.js library.
First install Testem:
Now create a folder called TDD anywhere in your system. We’re going to keep all our files in this folder.
Create the following files:
curry.js ( The module we’re going to implement )
curry-spec.js ( Test file for the curry.js )
testem.yml ( Testem configurations )
Edit the testem.yml and set up the source files for running the tests.
Open Terminal and run the command testem from the TDD folder to verify testem is running.
Mocha and Chai
As I said earlier, we’re going to use Mocha and Chai for writing the test cases. By default, testem uses Jasmine as the testing framework. So to use Mocha and Chai, we need to change the testem configuration.
First install Chai.js from npm.
Open the testem.yml in editor and modify like below.
We also need to override the global expect in Testem with the expect method of Chai.js
Create a setup.js file in TDD folder like below:
To make sure that everything works fine, edit the curry-spec.js with the following code and run the testem command from terminal.
Open the link in the terminal to start capture the browser and run the tests. Once the tests ran, you should be able to see an output like this:
Thinking about Behaviours of the Module
Behaviour of the module is the output it produces for various inputs. Or we can say, how the module is responding to various scenarios. These scenarios are going to be our test cases. So, before we start writing our test cases, we should think about all possible scenarios the module can handle with.
According to Wikipedia, curry function is defined as:
In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application).
So by definition, a function add(a,b,c) is a curried function if the add function supports following:
So, what we’re going to implement now is a function that can create a curried function from any other function. We’ll divide our test cases into two groups: one for curry function generator and another one for the actual curry function.
Writing the first test case
Let’s start with the curry function generator. We’ll name it as makeCurry. Its behaviour is to transform any function into a curried function.
Edit the curry-spec.js and remove the test code we already added. Start with a new describe block. The curry-spec.js should be like this now:
Run the testem command ( if it’s already running, you can see the following output in the browser captured )
We can see that the test is failing. The whole TDD starts from a failing test, and by making it pass. So why waiting, let’s implement the makeCurry function inside the curry.js file.
Now in the testem window, we can see another failure:
Modify the makeCurry function to return a dummy function as its output.
Our first test should pass now. By far, we have implemented the test case for the simple makeCurry function and implemented the makeCurry function.
This is how the TDD works. In the next section, we’ll see how the whole unit implementation is evolved in series of test cases and the code to make them pass.
Evolving the Final Code
For now, we have only one test case for the makeCurry function. Let’s think about different scenarios for our curry function.
Our curry function should always accept one function as parameter, if there is no function provided, it should throw an error.
Add the another test case inside the curry-spec.js
Now we can see that our test case is failing:
This is because we haven’t added the code in our makeCurry function to validate the arguments for it. Let’s implement this functionality now:
Now we’re getting into the rhythm of TDD. For now, we’re done with the expectations for the makeCurry function. Let’s implement the real curry function logic.
We are going to add a new describe block for the expectations for curry function. Here, the curry function is the one makeCurry returns. I am going to list down all the expectations for the curry function here. We’ll take one by one from the list and add to the test and implement the logic.
Expectations for the curry function:
We should be able to call the curried function as the original function provided. Ie, if the original function is add(1,2,3) the curried function curriedAdd(1,2,3) should behave same as add function. Like this:
If we call the curried function with the lesser number of arguments, it should return a function
When the total number or arguments is equal to or greater than the original number of arguments, it should return the results
We should be able to make any number of independent curried functions using makeCurry function.
We came up with all the test scenarios for our curry function. Let’s pick one by one and implement the logic. Add the first test case inside the new describe block.
Testem output should be like this:
Let’s make the test pass by adding the implementation:
Okay now, tests are passing. Let’s add the next test case inside the “Curry function” describe block:
Tests are failing now. Time to make them pass.
Now we need to verify that the number of arguments passed is less than the original number of arguments. If it lesser, the tests are expecting a function to be returned ( than executing the original function ). Let’s implement this:
All the tests are passing now. Let’s pick the next test case.
And the implementation:
Now, we’re going to add our last test case.
And this time, Testem reports that all tests are passing. Voila!, it means we’re done with our curry function implementation.
Here is the final code for curry-spec.js and the curry.js.
I wouldn’t say TDD is nice and simple. Writing all the code in TDD way is tough and frustrating for beginners. But if you keep practicing TDD, eventually you are going to love it. TDD will help you to concentrate on a small part of your code and complete it with perfection. Finally, that will lead to implement a better design for your actual module. So my point is, TDD is useful for better architecture of your code and the code will be less error prone.
Thanks for reading. I hope this will help for implementing TDD for your next project. Feedbacks are welcome.