“Testing is not responsible for the bugs inserted into software any more than the sun is responsible for creating dust in the air" - Dorothy Graham

When we write a function we already know what it should do, how many parameters it accepts and which result it returns.

In Development, we run our function and check the given output and compare it to the expected one, if something went wrong we fix the code and re-run until we get the correct output.

Testing our code this way is painful, and it’s not granted that it always returns the correct outcome for all use cases. it’s easy to miss something.

Let’s say that we created a function F(), while running our code in development F(1) gave us the expected output but F(2) gave us an unexpected result! We fixed the code, now F(2) works correctly, but F(1) may be affected with these changes!

In development, we have in mind many possible use cases but it will take a long time to test them all manually, and specially after every change! So it happens that we break something while fixing another thing!

That’s why automated tests are here.

Automated tests are code written separately, this code runs our functions in various ways and compares results with the expected.

Let’s take some examples:

Development of add function specifications:

We want to define a function add(x,y) that adds two numbers x and y. Before developing this function, we already know what it is supposed to do so we can describe it easily.

This description is called specification (spec) which contains use cases of our function and their tests, it looks like this:

describe("add", function() {

  it("adds two numbers", function() {
    assert.equal(add(2, 3), 5);
  });

});

A spec has three main blocks:

1. describe("add", function() {...}: What are we describing, in our case we are describing “add” functionality. A describe block can contain many “workers” = it(“title”, function(){}{}); blocks.

2. it("adds two numbers", function() {...}): we pass to it a detailed description about the use case we are testing now, and the second argument is a function that tests it.

3. assert.equal(add(2, 3), 5):

● This code inside it will be executed without errors if the implementation is correct.

● assert is used to check if add works as expected, here we are using assert.equal(add(2,3), 5) to check whether the value returned by add(2,3) is equal to 5 or not, if true that means our function add works as expected else, an error will be thrown and we have to consider our add function implementation.

Running the spec:

In this article we will be using these libraries for testing:

● Mocha: It gives us the describe and it testing functions.

● Chai: An assertion library for node and the browser that contains many assertions, and from it we will get assert.equal

All these libraries are fitting with the browser and server-side testing. Here, we will go with the browser.

Let’s create and html file called index.html:

<!DOCTYPE html>
<html>
<!-- add third-party libraries and styles for tests. -->
<head>
  <!-- add mocha css, to show results -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css">
  <!-- add mocha framework code -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
  <script>
    mocha.setup('bdd'); // minimal setup
  </script>
  <!-- add chai -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
  <script>
    // chai has a lot of stuff, let's make assert global
    let assert = chai.assert;
  </script>
</head>

<body>
 <!-- with the function to test, in our case – with the code for add. -->
  <script>
    function add(x, y) {
      /* function code is to be written, empty now */
    }
  </script>

  <!-- our case an external script test.js that has describe("add", ...) from above. -->
  <script src="test.js"></script>

  <!-- the element with id="mocha" will used by Mocha to output results -->
  <div id="mocha"></div>

  <!-- tests are started by the command mocha.run(). -->
  <script>
    mocha.run();
  </script>
</body>

</html>

Then, let’s create a file test.js in the same directory that contains the code we’ve written to test our add function:

describe("add", function() {

  it("adds two numbers", function() {
    assert.equal(add(2, 3), 5);
  });

});

After running our index.html in chrome, the result will be:

The test fails, and the raised error was `AssertionError: expected undefined to equal 5` and that’s logic because we don’t have any code in our add function yet so it returns undefined instead of 5 which was expected from our test code.

Let’s add some code to our function, in index.html our script tag that wrap our JavaScript code should look like this:

<script>
    function add(x, y) {
      return x + y;
    }
  </script>

Now our function has the right code that returns the sum of two numbers, let’s run the tests and check.

The test passed with success, let’s write more assertion with different examples to check that our function will always return the correct result whatever is receive as parameters:

Your test.js should look like this:

describe("add", function() {

  it("add : 2 plus 3", function() {
    assert.equal(add(2, 3), 5);
  });
  
   it("add : 4 plus 3", function() {
    assert.equal(add(4, 3), 7);
  });
  
   it("add : 0 plus 3", function() {
    assert.equal(add(0, 3), 3);
  });
  
   it("add : 2 plus -3", function() {
    assert.equal(add(2, -3), -1); 
  });
  
   it("add : 70 plus 10", function() {
    assert.equal(add(70,10), 80);
  });

});

We made every addition operation as a standalone use case, and after running index.html the output will be:

All tests passed successfully mean that the result of our function after performing addition is equal to the expected value that must be returned.

Try playing around with these cases or writing new ones for multiplication, subtraction or any other operations , you will learn by getting your hands dirty ;)

Nested Describe:

What if we want to test our function with more tests 15 tests for example, is it a good practice to write them all this way:

it("add : 2 plus 3", function() {  // Test N° 1
    assert.equal(add(2, 3), 5);
});
.
.
.
.
.
.
it("add : 12 plus 4", function() { // Test N° 15
    assert.equal(add(12, 4), 16);
  });

Absolutely not, it’s a bad practice!!

A helper function will be the best for this case, we will create a function that will generate all test cases for us and we should only invoke it. Let’s see some code:

// test.js 

describe("add", function() {

  describe("adds x to y", function() {

    function makeTest(x, y) {
      let expected = x + y;
      it(`${x} plus ${y} is ${expected}`, function() {
        assert.equal(add(x, y), expected);
      });
    }
    let y = 2;
    for (let x = 1; x <= 15; x++) {
      makeTest(x, y);
	  y *= 2;
    }

  });
});

The nested describe defines a new “subgroup” of tests, and our helper function makeTest was used to generate test cases; the for loop will produce values of x in [1,15] and we are starting with y equal to 2 and multiplying by 2 after every test case is runned. This way we will have different (x,y) values that you’ll see them in the output:

All tests are successful.

With the spec, we can securely improve, change, even rework the function and ensure it actually works right.

That is particularly significant in enormous projects when a function is utilized in many places. When we change such a function, it will be very difficult to manually check if every place that uses it still works right.

Without tests, people have two ways:

1. Make changes without being able to test all their side effects on your functions, and then users will face bugs that will be difficult to debug.

2. Code extensibility will be hard, and this is not good for development.

For More knowledge check:

https://mochajs.org/#features

https://www.chaijs.com/guide/