Test Driven Development by Example
Tags: javascript, tutorial, webdev, programming
Published: 2020-05-10 18:54:05

---
title: Test Driven Development by Example
published: true
description: build a throttle function with TDD
tags: javascript,tutorial,webdev, programming
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/on5dhtt8k5p1ut6im1c5.png
---

Among the few positive aspects of the lock-down, having more time to read is definitely one of them. Two weeks ago I started reading again the Test Driven Development (TDD) bible written by [Kent Beck](https://en.wikipedia.org/wiki/Kent_Beck), who is considered by most the father of TDD. Regardless of what your thoughts are about TDD, the book is a gold mine on testing. I highly recommend it.

In the same spirit of the book, this article is going to be a practical walk through on how to develop code driven completely by tests; an example from start to end on how to apply TDD. I am going to start with a brief recap on TDD, then I'll walk you through an example in which we are going to code a __throttle__ the TDD way. Last, I'll share some resources that can be used to practice TDD.

The target audience for this post is people who are considering using TDD in the development process. If you have already delved into TDD or you are already using it, this post probably won't add any new information to your knowledge. However it might still be useful as a reference you can share with others curious about the topic.

## Preface
TDD is one of the software engineering practice which has stood the test of time. At the beginning of 2000s Kent Beck came out with the book "[Test Driven Development: By Example](https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530)". The book is twenty years old, though TDD as a concept it's probably older than that. It was Kent Beck himself to say that he did not "invent" TDD, but rather "rediscover" it from old articles and papers. [The humble programmer, Dijkstra (1972)](https://dl.acm.org/doi/pdf/10.1145/355604.361591?download=true) and the [Report of The Nato Software Engineering Conference (1968)](http://homepages.cs.ncl.ac.uk/brian.randell/NATO/nato1968.PDF) both described the process of testing the specification _before_ writing the code. While, Kent Beck might not have been the one who invented, he definitely was the one who made it popular.

### Is a 20+ engineering practice still relevant today?
Everything we do is built upon layer of abstractions and decisions made decades ago. People who made those decisions lived in a different context, had different constraints and problems to solve. What they did, is what we do today: they came up with the best solution they could think of at the time.
Their decisions live with us. But most often, their reasons do not. 
Technology changed, the problems we need need to solve changed, the world has changed. 

As software engineer one of the most valuable skills I picked up is to question everything, understand why things are the way they are. Searching for the context in which these decisions were made is the key to understand if the same decisions are applicable in the current world.

So, is TDD still relevant today? I think it is, because:
- we still need to write unit tests to prove that our code respect the specification
- we still want to reduce the number of bugs which make it all the way to production 
- we still want to iterate fast and integrate changes often
- we still want to build highly cohesive and loosely coupled components 

I believe the premises of TDD are still valid in the context we live in. 

### TDD is controversial
Not everybody thinks TDD is useful. I couldn't agree more - not everybody must use it. During the years, a few research studies were made to determine the effectiveness of TDD in the software development process, but they were largely inconclusive. I think that's because quantitative measurements about source code quality and speed of iterations are too noisy and dependent on social factors - all things which are difficult to take into account in a research study. 

I want to conclude this quite lengthy preface by saying that I am not religious about TDD - and I hope neither will you. It's like any other tool we have in our toolbox - it allows to see the problem from a different point of view.

## TDD

TDD is a predictable way to develop code which relies on the following three steps:

1. **RED** - Write a unit test, run it an watch it fail. 
The unit test should be short and focus on a single behavior of the system under test. By writing the failing test you ensure that your test is calling the correct code and that the code is not working by accident. It's a meaningful failure, and you expect it to fail
2. **GREEN** - Write the minimum amount of code needed to make the test pass
3. **REFACTOR** - Eliminate the duplication (both in the test and in the code, including duplication between test and code). More in general this is the step in which you would perform refactoring

![TDD cycle](https://dev-to-uploads.s3.amazonaws.com/i/hcwwu30olhngky9pvp7d.PNG)

There isn't much else you need to know to start using TDD. Using it effectively  is just a matter of practicing it over and over again. Project after project you become better at it.

### Why TDD?

- you are always one test away from functional code
- tests are more expressive; the result is usually tests that cover the behavior of the module instead of the underlying implementation
- increased test coverage and reduced coupling between test and production code
- it's very useful when you know what you have to build, but have no idea where to start; a situation quite common when you need to add or change a new feature in a piece of the code base you are not familiar with

## Throttling  example

In this section we will build a _throttle_. The end goal of throttling is to limit how many times a function can be called in a given interval of time. It's generally used to avoid overloading the receiver with too many calls (for example a remote server) or because a sample of the events is sufficient to carry on with the functionality.   

To sum it up *to throttle a function* means to ensure that the function is called at most _X_ times in a specified time period (for instance, at most three times every second). The throttle we are going to build, is a slightly simpler version which only allows at most _one_ call in a specified period. This is the spec:

```
throttle returns a function which is called at most once in a specified time period. 
It takes as input the function to throttle and the period. 
If the period is less or equal than zero, then no throttle is applied.
```

Let's try to build it. Since we are using TDD, this means writing our test first.

#### First test

```javascript
    describe("Given the throttle time is 0", () => {
        it("Runs the function when we call it", () => {
            let count = 0;
            const 
                fun = () => count++,
                funT = throttle(fun, 0);
            funT();
            expect(count).toBe(1);
        });
    });
```
In the test, we defined a simple function called _fun_ which simply increments a variable called _count_ every time we invoke the function. We call our _throttle_ function giving it as parameter the function we just defined and a throttle period of zero. According to the specification, if the throttle period is zero, the function must be invoked when we call it. We called _funT_ (as in fun Throttled) the result of applying _throttle_ to _fun_.

Run the test and watch it fail. Now, we have to make it pass by writing the minimum amount of code necessary. So. let's create the _throttle_ function: 

```js
function throttle(fun, throttleTime) {
    return () => {
        fun();
    }
};

module.exports = { throttle };
```

Run the test again, and it's green! To make the test green, we just had to create the _throttle_ function and make it invoke _fun_. At this point there is nothing to refactor, so we'll move the next test.

### Second test

According to the spec, if the throttle period is zero, the function must be invoked "every" time we call it because no throttle is applied. Let's test that:

```js
    describe("Given the throttle time is 0", () => {
        it("Runs the function 'every' time we call it", () => {
            let count = 0;
            const 
                fun = () => count++,
                funT = throttle(fun, 0),
                calls = 10;
            for (let i = 0; i < calls; i++) {
                funT();
            }    
            expect(count).toBe(calls);
        });
    });
```

Instead of calling _funT_ once like in the previous test, now we are calling it ten times and we expect the _count_ variable to be ten at the end.

Run the tests and...it's green. We did not even have to add any code for it, good. Before we go ahead with the next test, we are going to refactor: the second test includes the first one so we can remove it, which leaves us with the following suite:

```js
describe("throttle suite", () => {

    describe("Given the throttle period is 0", () => {
        it("Runs the function 'every' time we call it", () => {
            let count = 0;
            const 
                fun = () => count++,
                funT = throttle(fun, 0);
                calls = 10;
            for (let i = 0; i < calls; i++) {
                funT();
            }    
            expect(count).toBe(calls);
        });
    });
});
```

#### Third test

Let's add another test when the throttle period is negative:

```javascript
    describe("Given the throttle period is negative", () => {
        it("Runs the function 'every' time we call it", () => {
            let count = 0;
            let count = 0, calls = 10;
            const
                fun = () => count++,
                funT = throttle(fun, -10);
            for (let i = 0; i < calls; i++) {
                funT();
            }    
            expect(count).toBe(calls);
        });
    });
```

Again, it passes and we did not have to add any code. We can refactor since the test for the negative period and the zero period are very similar:

```js
describe("throttle suite", () => {

    const runFun = (throttlePeriod) => {
        it("Runs the function 'every' time we call it", () => {
            let count = 0, calls = 10;
            const 
                fun = () => count++,
                funT = throttle(fun, throttlePeriod);
            for (let i = 0; i < calls; i++) {
                funT();
            }    
            expect(count).toBe(calls);
        });
    };

    describe("Given the throttle period is 0", () => runFun(0));
    describe("Given the throttle period is negative", () => runFun(-10));
});
```

#### Fourth test

```js
describe("Given the throttle period is positive", () => {
        describe("When the throttle period has not passed", () => {
            it("Then `fun` is not called", () => {
                let count = 0;
                const
                    fun = () => count++,
                    funT = throttle(fun, 1* time.Minute);

                funT();
                expect(count).toBe(1);
                funT();
                expect(count).toBe(1);
            });
        });
    });
```

Run the test and watch it fail:

```
Failures:
1) throttle suite 
   
   Given the throttle period is positive 
   When the throttle period has not passed 
   Then `fun` is not called
     Message:
       Expected 2 to be 1.
```

What's happening here? We expect the first call to _funT_ to go through because the throttle does not apply to the first call. Thus in the first expectation we check if the variable _count_ is equal to one. The second time we call _funtT_ must be throttled because at least one minute needs to pass between the first and the second call; that's why we expect _count_ still to be one in the second expectation. Except it isn't. The _count_ variable is two because we haven't implement any throttling logic yet. 

What's the smallest step to make the test pass? What I have come up with is:
- check if it's the first time we are calling the function 
- differentiate between a positive throttle period and a less than zero period

```js
function throttle(fun, throttleTime) {
    let firstInvocation = true;
    return () => {
        if (throttleTime <= 0) {
            fun();
            return;
        }
        if (firstInvocation) {
            firstInvocation = false;
            fun();
        }
    }
};
```

The introduction of `firstInvocation` and the `if statement` was enough to make the test pass.

#### Fifth test

Next one is interesting.

```js
        describe("When the throttle period has passed", () => {
            it("Then `fun` is called", () => {
                let count = 0;
                const
                    fun = () => count++,
                    funT = throttle(fun, 1* time.Minute);

                funT();
                expect(count).toBe(1);
                // 1 minute later ...
                funT();
                expect(count).toBe(2);
            });
        });
```
In this test we want to verify that after one minute has passed, the function won't be throttled. But how do we model time? We need to have something that allows to keep track of time, like a timer or something similar. More importantly, we need to manipulate the state of the timer in the test. Let's assume we already have what we need and change the test accordingly:

```js
        describe("When the throttle period has passed", () => {
            it("Then `fun` is called", () => {
                let count = 0, timer = new MockTimer();
                const
                    fun = () => count++,
                    funT = throttle(fun, 1 * time.Minute, timer);

                funT();
                expect(count).toBe(1);
                // fast forward 1 minute in the future
                timer.tick(1 * time.Minute); 
                funT();
                expect(count).toBe(2);
            });
        });
```

The diff between this version of the test and the previous is the introduction of the _MockTimer_. It's initialized with the rest of the variables at the beginning of the test. Right after the first expectation the timer _tick_ method is called to move the timer one minute in the future. Since the throttle timeout is one minute, we expect the next call to _funT()_ to go through. 

Let's run the test. Not surprisingly, it fails because the MockTimer does not exist. We need to create it. 

Before doing that, let's figure how we would use the timer in the throttle function. You can come up with different ways of using it. In my case I decided I needed to have a way to start the timer and check if it is expired or not. With that in mind let's change the _throttle_ function to make use of a Timer that does not exist yet. Using a function before implementing it seems stupid, but in fact it's quite useful because you get to see the usability of the api before writing the code for it.

```js
function throttle(fun, throttleTime, timer) {
    let firstInvocation = true;    
    return () => {
        if (throttleTime <= 0) {
            fun();
            return;
        }
        if (firstInvocation) {
            firstInvocation = false;
            fun();
            timer.start(throttleTime);
            return;
        }
        if (timer.isExpired()) {
            fun();
            timer.start(throttleTime);
        }
    }
};
```

Established the api, let's implement a mock timer for our test:

```js
class MockTimer {
    constructor() {
        this.ticks = 0;
        this.timeout = 0;
    }

    tick(numberOfTicks) {
        this.ticks += numberOfTicks ? numberOfTicks : 1;
    }

    isExpired() {
        return this.ticks >= this.timeout;
    }

    start(timeout) {
        this.timeout = timeout;
    }
}
```

Run the test again, and boom, tests are green!

Let's change our test and make it richer:

```js
describe("When the throttle period has passed", () => {
    it("Then `fun` is called", () => {
        let count = 0, timer = new MockTimer();
        const
            fun = () => count++,
            funT = throttle(fun, 1 * time.Minute, timer);

        funT();
        expect(count).toBe(1);

        timer.tick(1 * time.Minute);
        funT();
        expect(count).toBe(2);

        timer.tick(59 * time.Second);
        funT();
        expect(count).toBe(2);

        timer.tick(1* time.Second);
        funT();
        expect(count).toBe(3);

        for (let i = 0; i < 59; i++) {
            timer.tick(1 * time.Second);
            funT(); 
            expect(count).toBe(3);
        }

        timer.tick(1* time.Second);
        funT();
        expect(count).toBe(4);
    });
});
```

At this point, we just need to plug in an actual timer which we could build with a similar process, for example:
```javascript
class Timer {
    constructor() {
        this.expired = true;
        this.running = false;
    }

    isExpired() {
        return this.expired; 
    }

    start(timeout) {
        if (this.running) {
            return new Error("timer is already running");
        }
        this.expired = false;
        this.running = true;
        setTimeout(() => {
            this.expired = true;
            this.running = false;
        }, timeout);
    }
}
```

#### Tidying up the API

There is one last thing. We can create a default timer instead of requiring the caller to pass it as a parameter:


```js
function throttle(fun, throttleTime) {
    return throttleWithTimer(fun, throttleTime, new Timer());
}

function throttleWithTimer(fun, throttleTime, timer) {
// ... same as before
```

Finally we can use our throttle function:

```js
throttle(onClickSendEmail, 1 * time.Second);
```

## Practice TDD
If you like the idea of writing your test first, then give TDD a try. In this article I showed the _throttle_ function, maybe you can try the _debounce_ by yourself. When I was thinking about the article I almost settled on using [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) as example, but it did not take me long to realize that the article would be too long. If you are up for it, it's a fun exercise to build with TDD.

You could also try some of the programming [Katas](https://en.wikipedia.org/wiki/Kata_(programming)) available online, like:
- [code wars](https://www.codewars.com/)
- [code kata](http://codekata.com/) 
- [coding dojo](https://codingdojo.org/KataCatalogue/)

## Conclusion
Whatever you pick to flex your TDD muscle, my suggestion is to give it sometime. At least for me, TDD did not click right away. The first times I tried it, I got stuck - I could not figure how to write the test before the code. But I kept practicing on my own and ultimately it became natural to think about the test before thinking about the code.  

--------------------------------------------------

Follow me on [Twitter](https://twitter.com/napicellatwit) to get new posts in your feed.
Credit for the cover image to [GraphicMama-team](https://pixabay.com/users/graphicmama-team-2641041/)