A crash course on testing with Node.js

80,615 reads

@ adnanrahic Adnan Rahić Dev/Avocado at Sematext.com. Co-Founder at Bookvar.co. Author of "Serverless JavaScript by Example"

JavaScript is a beautiful language. You must believe I’m crazy. Maybe you’re crazy for agreeing with me. But why would I say something like this? As a language, JavaScript gives you no support whatsoever. It bites your head off if you give it the slightest chance, and it has bizarre error messages if left unhandled. So you tell me, why is it beautiful?

reactions

Because it creates good, responsible, and intelligent developers. By worrying about getting your head ripped off by the smallest mistake, you adapt and overcome. The skill gained has less in common with programming and much more with a programmer’s state of mind. Because you are getting used to not having an invisible force guide you through the code. Instead, you rely on yourself, and your own skill. Hence, me stating something as crazy as I did above.

reactions

Why then, does this create good programmers? A good programmer is responsible, meticulous and reliable. Programmers like these make sure their code works. No matter which environment, or which machine. These masters of their craft, always cover code with tests, to ensure their work is valid. They have my utmost respect. I would like to believe they have your as well.

reactions

Baby steps.

To lay the foundation of what a basic test case would look like let’s create a simple function.

reactions

function addTwoNumbers ( x, y ) { return x + y; } console .log(addTwoNumbers( 5 , 1 ));

Calling this function we can see the result is 6. Because we know basic math, it makes perfect sense. But what if the function is really complex?

reactions

Let’s make sure to write a test case to ensure the function is valid no matter what.

reactions

function addTwoNumbers ( x, y ) { return x + y; } function testAddTwoNumbers ( ) { var x = 5 ; var y = 1 ; var sum1 = x + y; var sum2 = addTwoNumbers(x, y); console .log( 'addTwoNumbers() should return the sum of its two parameters.' ); console .log( 'Expect ' + sum1 + ' to equal ' + sum2 + '.' ); if ( sum1 === sum2 ) return console .log( 'Passed.' ); console .log( 'Failed.' ); } testAddTwoNumbers();

See this? We’re defining the values to add, and creating their sum. Then we call

addTwoNumbers()

sum1

sum2

reactions

addTwoNumbers() should return the sum of its two parameters. Expect 6 to equal 6. Passed.

assigning it to another variable. Having done this, we’re ready to test the equality. What’re we expecting? Well,should be equal to, if the function we created works as expected. Running this piece of code you should see the following get logged to the command line:

Congratulations, you’ve written your first unit test! The act of unit testing lies in writing tests for small units of code. Hence the name. Meaning you’ll write individual test cases for validating the behavior of functions, methods, and objects. Exactly like we did above.

reactions

What if we add a deliberate bug to our code? For the heck of checking if the unit test will fail gracefully. Change the

addTwoNumbers()

reactions

function addTwoNumbers ( x, y ) { return x + x; // deliberate bug! }

function to:

Run the unit test once again and you’ll see it fail like it should.

reactions

addTwoNumbers() should return the sum of its two parameters. Expect 6 to equal 10. Failed.

A bit of theory.

A unit test consists of three parts.

reactions

Arrange Act Assert

From their names alone it is easy to comprehend what they stand for. Let’s break it down while looking at some code.

reactions

function addTwoNumbers ( x, y ) { return x + y; } function testAddTwoNumbers ( ) { // 1. ARRANGE var x = 5 ; var y = 1 ; var sum1 = x + y; // 2. ACT var sum2 = addTwoNumbers(x, y); console .log( 'addTwoNumbers() should return the sum of its two parameters.' ); console .log( 'Expect ' + sum1 + ' to equal ' + sum2 + '.' ); // 3. ASSERT if ( sum1 === sum2 ) return console .log( 'Passed.' ); console .log( 'Failed.' ); } testAddTwoNumbers();

In the first part we arrange all necessary preconditions and inputs. You can see we defined the variables to add and the sum of these variables. The second step is to act on the function, object or method under test. Lastly, we assert that the expected results have occurred.

reactions

You may find the word assert a bit overwhelming. As a non native English speaker, I sure did, when I first heard it. Not to worry, it only means to claim. You are asserting a truth, meaning you claim something is true. As simple as that.

reactions

Assertions are a tool to do basic sanity checking for programmer errors.

— Marijn Haverbeke, Eloquent JavaScript

Want to write your own assertion? Sure you do. Check this out.

reactions

var assert = { equal : function ( firstValue, secondValue ) { if (firstValue != secondValue) throw new Error ( 'Assert failed, ' + firstValue + ' is not equal to ' + secondValue + '.' ); } }; function addTwoNumbers ( x, y ) { return x + y; } function testAddTwoNumbers ( ) { // 1. ARRANGE var x = 5 ; var y = 1 ; var sum1 = x + y; // 2. ACT var sum2 = addTwoNumbers(x, y); console .log( 'addTwoNumbers() should return the sum of its two parameters.' ); console .log( 'Expect ' + sum1 + ' to equal ' + sum2 + '.' ); // 3. ASSERT try { assert.equal(sum1, sum2); console .log( 'Passed.' ); } catch (error) { console .log(error.message); } } testAddTwoNumbers();

On line 1 we instantiate a new object named

assert

equal

assert.equal()

'Passed.'

reactions

How about we get serious?

, immediately adding a method called. If the two passed parameters are not equal, the function will throw an error. That’s it, that’s all the logic in the whole method. Now, on line 27 we’re wrapping thestage in a try catch block, and calling themethod. Only if the values are not equal will an error be thrown and caught in the catch block. Otherwise, the thread of execution will continue and logto the console. Go ahead and try it out!

The examples above have shown the fundamentals of testing in general. Also pointing out the required mindset needed to succeed in the field of programming. It’s time to bring out the big guns. You’ll rarely ever use the code above in a production environment. Nevertheless, it’s crucial in the understanding of what is to come.

reactions

You can use many various tools to write tests for Node.js applications in production. An example is the built-in assertion library. Yes, Node does have assertions baked in. Only change line 1.

reactions

var assert = require ( 'assert' ); function addTwoNumbers ( x, y ) { return x + y; } function testAddTwoNumbers ( ) { var x = 5 ; var y = 1 ; var sum1 = x + y; var sum2 = addTwoNumbers(x, y); console .log( 'addTwoNumbers() should return the sum of its two parameters.' ); console .log( 'Expect ' + sum1 + ' to equal ' + sum2 + '.' ); try { assert.equal(sum1, sum2); console .log( 'Passed.' ); } catch (error) { console .error( 'Failed.' ); } } testAddTwoNumbers();

By changing out our custom assert object for the built-in Node module our code works exactly the same. The default assertions in Node are extremely powerful, you can take a longer look at them here.

reactions

However, tools like Mocha and Chai are the bread and butter of testing Node.js applications.

reactions

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.

— mochajs.org

Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.

— chaijs.com

Let’s check this out. First of all you’ll need to init a new Node project by hooking it up to npm.

reactions

Open up a terminal window in your directory of choice, and run:

reactions

npm init

Please feel free to enter through all of the prompts. When you’ve done that, you’ll need to install the required modules.

reactions

npm install -- save - dev mocha chai

Now you can open up your code editor of choice and start by adding files like so:

reactions

> test - test.js - addTwoNumbers.js

One test directory with a test.js file, and another file named addTwoNumbers.js in the root of the directory. Go ahead and paste the

addTwoNumbers

reactions

function addTwoNumbers ( x, y ) { return x + y; } module .exports = addTwoNumbers;

function into the addTwoNumbers.js file like so:

Don’t forget to export it to be able to require it later on. Now we can start with the fun part. Open up test.js and start by laying the foundation for our tests.

reactions

var expect = require ( 'chai' ).expect; var addTwoNumbers = require ( '../addTwoNumbers' ); describe( 'addTwoNumbers()' , function ( ) { it( 'should add two numbers' , function ( ) { // 1. ARRANGE var x = 5 ; var y = 1 ; var sum1 = x + y; // 2. ACT var sum2 = addTwoNumbers(x, y); // 3. ASSERT expect(sum2).to.be.equal(sum1); }); });

At the beginning of the file we need to require both Chai and addTwoNumbers. Look at the way we required Chai, only grabbing expect. Chai comes with three types of interfaces for creating assertions. They are all valid. Which one you choose is only preference. I feel like expect suits me just fine. Don’t get mindblown by the test syntax. It’s created to simulate natural human speech patterns. The describe block creates a test environment. The

it

addTwoNumbers()

reactions

blocks defines test cases which need to pass. Reading it out loud sounds rather fine. Describe, it should add two numbers. Makes perfect sense! Can you now see why testing is important apart from making sure the code works? A test is in itself documentation. Writing a test will explain what the code does. Every other developer working on the code base will have no issue understanding it in no time.

All that’s left is to run the tests. Add

"test": "mocha"

reactions

Hint, your package.json should look like this:

{ "name" : "testing" , "version" : "1.0.0" , "description" : "" , "main" : "test.js" , "directories" : { "test" : "test" }, "scripts" : { "test" : "mocha" }, "author" : "" , "license" : "ISC" , "devDependencies" : { "chai" : "^4.1.1" , "mocha" : "^3.5.0" } }

in the scripts section of your package.json and you’ll be ready to go!

Jump back to your terminal window and run

npm test

reactions

Taking it all in.

. You’ll see an awesome interface with some green text saying there is 1 passing test!

You’ve now experienced the natural process of covering code with tests. All the examples have been showing unit tests, which is more than enough to begin with. When you get comfortable with these concepts, understanding integration and end-to-end testing will be like a walk in the park. But that’s a topic for another article.

reactions

I urge you to continue playing with these testing tools. Try to include them into your existing development process. You will see an overall improvement in code quality and mental health. Trust me, having peace of mind with a completely green test suite does wonders for the nerves.

reactions

If you want to take a look at all the code we wrote above, here’s the repository. Or if you want to read my latest articles, head over here.

reactions

Latest stories written by Adnan Rahić - Medium

Read the latest stories written by Adnan Rahić on Medium. Software engineer @bookvar_co. Coding educator @ACADEMY387…medium.com

reactions

Hope you guys and girls enjoyed reading this as much as I enjoyed writing it.

Do you think this tutorial will be of help to someone? Do not hesitate to share. If you liked it, click the clap below so other people will see this here on Medium.

reactions

Tags