Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

19 Best Practices for Automation Testing With Node.js

DZone's Guide to

19 Best Practices for Automation Testing With Node.js

Learn how you can deliver robust web applications.

· Performance Zone ·
Free Resource

SignalFx is the only real-time cloud monitoring platform for infrastructure, microservices, and applications. The platform collects metrics and traces across every component in your cloud environment, replacing traditional point tools with a single integrated solution that works across the stack.

Node.js has become one of the most popular frameworks in JavaScript today. Used by millions of developers to develop thousands of project, Node.js is being extensively used. The more you develop, the better the testing you require to have a smooth, seamless application. This article shares the best practices for testing Node.js in 2019 to deliver robust web applications.

Let's say you developed an application in Node.js about the weather forecast. Testing Node.js for a weather forecast application is very complex due to numerous modules and features. For example, a web application will tell you the forecast for today, and along with it, it will show you different parameters such as precipitation and humidity. This application will also require location tracking since every user is located at a different location. A user using weather applications needs to have correct data because many things work on the weather of the day and of the subsequent days, like planning a trip, perhaps.

A complex weather application might include winds and cloud movements for the geologists to study. An application used at such a level cannot contain a single glitch by which the complete country's future is predicted. So after the development of these modules, we get to testing Node.js for these modules so that everything is checked and works fine in real time.

Hold on now! We are not going to perform testing with Node.js on a weather application in this article. However, I will demonstrate a simple project where we initialize a test in Node.js in small steps so that it becomes clear in the end about how the tests are created for testing. We will focus on the best practices for testing Node.js in 2019.

Initializing a Simple Test in Node.js

If you are new to Node.js, then here I will initialize a simple test in Node.js for you to understand the procedure of testing in Node.js. Let us create a basic function of multiplying two numbers:

function mul(x,y)
{
return x*y;
}


This is a basic function for multiplication. But, this function is not valid for every case and can stand invalid for various other numbers. We cannot perform testing in such a situation. We will extend this function to validate the equality feature.

function mul(x,y)
{
return x*y;
}
function testMul()
{
var x = 4;
var y = 5;
val mul = x*y;
var mul2 = mul(4,5);
if(mul == mul2)
console.log(‘Equality Test Passed’);
else
console.log(‘Equality Test Failed’);
}
testMul();


After writing the basic tests, we will initialize the new node project by hooking it up to the npm:

npm init


Now, you need to install the modules. As we discuss further in this article, you will learn more about the importance of Mocha and Chai in Node.js testing. Mocha gives you a good platform to define your test case suites, hooks, and use assertion of whatever library you want to use. Chai, on the other hand, is used for writing the assertions only. These assertions are the human-readable test codes, which has advantages of its own.

npm install --save-dev mocha chai


After this, I have created two js files. One being test.js and other mull.js . Save the test.js in the test folder and mull.js in the root directory.

Now, paste the function mul written above with an extra line.

function mul(x,y)
{
return x*y;
}
module.exports = mul;


Exporting it will help us in adding it to the required line as we perform testing with Node.js.

Open your test.js file in the tests folder and write the following test code given below to go-ahead testing with node.js. This test code will test the equality of the two functions. The expect variable is the object code from the Chai library to write the "expect" part of the assertion, and multiply will be the variable that contains the exported module of the code given just above it (module.exports = mul).

var expect = require(‘chai’).expect;
var multiply = require(‘../mul’);
describe(‘mul()’, function(){
    it(‘should multiply two number’, function(){
var x = 4;
var y = 5;
val mul = x*y;
var mul2 = mul(4,5);
expect(mul2).to.be.equal(mul)
});
});


Just run your tests now through npm test in the console window and you are good to go.

Best Practices for Testing Node.js

Testing assesses the stability of your application, and at a minimum, makes your app more stable, which, in turn, saves you from a sudden wrong commit, which can take the whole software down with it. Testing is required before you push your code to your users so that no one is bugged by any of the unwanted behavior of the application. Being this important, we will see some of the best practices for testing Node.js in 2019.

Isolated and Atomic

The test should be atomic and isolated. Every test should run independently and without being dependant on each other. If neither test is dependent on any other test, then if one test fails, other tests are not affected. Also, the tests should follow the atomicity property. It should not fail in between suddenly. A test should be entered and exited with the pass or fail result smoothly.

You should also keep in mind that the data you are testing upon should be separate for every test. More than one test working on the same global data harms the overall motive of using the tests upon the application. Your time will absolutely increase, leading to good performance, but it is of no point — keep your data specific to the tests.

Naming of Your Test

This is one of the most basic and important features for writing effective test cases. A test should be named meaningful and easily understood by other departments who are not related to testing, such as a development team. A name should not be any random name, such as foo(), which is being popularly used. After seeing that your test is no random word, you should focus on what you should name your test. A test name should constitute of:

  • What is being tested
  • Different scenarios under which you are testing
  • The expected result of the test

Here is an example of a meaningful naming convention for testing Node.js.

function CheckCountryLanguage()
{
//code to check if the application is showing data in the country’s official language
}


The above test name makes sense because we can easily get what that function would be doing. What if I had written the function name as foo? Then, I would have to read the complete code to understand the working of the function.

Using Assertions

An assertion in the programming language is a statement for which we declare at the time of coding. This declaration may or may not be true and, hence, provides the boolean output with true or false. The declaration itself contains the meaning of the test code, such as expect('age').to.be.equal(23). This is self-explanatory and cuts the code lines' logic to a great extent. If the variable 'age' is equal to 23, then True is printed, or else False. You can learn more about the assertion here. Assertions are more beneficial than the normal tests because they already provide a statement in the test case. Also, when an assertion is run, you don't need to know what was the response and why you got it. It would just provide you whether the test failed or passed.

A test will use logic in test cases while assertions are the human-readable forms where you write tests in human-readable forms. This helps when analyzing the tests after they are run. You can use Chai library for achieving the same and can learn more about the Chai library here.

expect(‘currentweather’).to.be(‘string’);


Such assertions are human-readable and self-explanatory about the test they are performing. This expect shows that the currentWeather has to be a string such as Hazy, Clear, or Rainy.

Use Test Runner

A test runner is a library or a set of tools that takes a source code directory that contains the unit tests and runs tests on it. After executing the tests, it writes the results back on the console or the log files. It is always recommended to use a good test runner, and some of the testers use their own test runners, too. While having a test runner can be advantageous with the databases, it can take the database values (dummy or real) and execute different tests on it. It can load fixtures, too. Mocha is a test runner. Mocha can provide you a programmatic way to run the tests via command line tools on ES6 codebase.

Focus On Test Coverage

A test coverage, while writing the tests, is the amount of source code you are covering in your test. In simple words, it can also be the amount of the application as a whole that you are covering for your test. While writing the tests, it is considered the most important thing to work upon. So how can you increase your test coverage?

First of all, you should always keep in mind that the test coverage percentage is totally dependant upon the nature of your application. If it is some application — say Music Player — then it need not have 100 percent test coverage because, as we increase the test coverage, it becomes more and more expensive for the company. But if you have an important application, like any real-time application receiving data from satellite or an application for the manufacturer of airplanes, then you need to have 100 percent coverage because it will affect the application to a great extent. We will focus on test coverage for the upcoming points. For this, you can use Mocha along with Istanbul and run your Mocha tests over Istanbul.

Use Plugins for Test Coverage

Plugins are available to test all of the test coverage. Plugins will not help you in writing the tests that cover maximum code, but it will definitely help you analyze your test and tell you if a test is skipped or not. It will also show whether test cases are covered or not. This way, you might be thinking that you have written the tests that cover some percentage of the code, but in reality, some tests are skipped, bringing down the overall percentage.

Analyze the Test Coverage Report

A test coverage report can be generated with the help of Istanbul and Mocha. Once you have generated the test coverage report, try to analyze the report. A report with say 90 percent test coverage that you might think will not be covering the complete 90 percent code with its test cases. While using Istanbul, it is quite easy and straightforward to analyze the test coverage report. Also, you should take the failed test seriously and analyze them as to why did they fail and check if there is an issue or not.

Use Mutation Testing

Mutation testing is the type of testing in which the test cases' logical condition is tweaked (mutated) to deliberately fail the tests, or if it failed, then to pass it. The logic can also be changed for the same reasons. It is also popularly called as planting a bug. There are various libraries that you can get on the Internet, but one of them, and most popular, is Stryker, which can be used for the same purpose.

Check Plagiarism on Tests

While testing Node.js on the source code, you should always check for plagiarism on the code. It is not so uncommon to copy and paste the code from the Internet to make the software work. You might never know the source code can be licensed and your organization can fall into serious trouble for the same. These things violate copyright issues. So, always remember to check the code for plagiarism in order to tweak the code a little — but for the advantage of your organization.

In order to use the plagiarism checker, you can install the node.js npm plagiarism-checker package.

To install it, just type the following code:

npm i plagiarism-checker


After that, in order to use that SDK, type the following code:

var a = require('plagiarism-checker');
var b = new a();
var config = b.getConfig();


Download the code from this repository and add it to your project. Keep in mind the following dependencies are installed:

$ npm install lodash
$ npm install request
$ npm install request-promise
$ npm install mime-types


Use Realistic Inputs

Many times, it so happens that the testers use the inputs that are not realistic or practical to the real-life scenarios. For example, an input demanding phone numbers should be tested on numbers which resemble the real phone numbers. So, you should always use realistic inputs while testing your application. The best library available for this purpose is Faker library. According to GitHub, Faker library is a PHP library that generates fake data for the input you are willing to test upon.

You should also keep in mind to use more and more inputs for a single input parameter so as to test heavily. As in real life, many inputs will be processed on the same function, you should use as many as possible to test them.

A simple portrayal of the situation can be the same weather application that we discussed at the beginning of this chapter.

function CountryName(string country)
{
//code
}


Now, this function should be tested with realistic inputs, such as:

function CountryName(India)
{
//code
}
function CountryName(Netherlands)
{
//code
}


Instead of :

function CountryName(abc)
{
//code
}
function CountryName(foo)
{
//code
}


Use Linters

According to Wikipedia, linter is a tool that analyzes the source code to flag programming errors, bugs, stylistic errors and suspicious constructs. Linters are great at finding certain classes of bugs including assignment to an undeclared variable and use of undefined variables. Using linters can help you a lot in determining the bugs in the structural way of the code. For Node.js, you can use ESLint for the same purpose.

Property-Based Testing

Property-based testing depends on different properties of the function. It is used to check the property of the entity (function, program etc) in particular. A property is a characteristic of the entity. For example, if you have a function that takes the input arguments as a,b and holds the property that b is always even then by property based checking, we check whether b is always even or not.

For all(a,b), b is always an even number.

Property-based testing helps us:

  • Contains the scope of all inputs and that can generate a huge number of test cases
  • Can take us to the failure in a very short time as it has something specific to put as the input like even numbers in the above case. It can keep on inserting even numbers until a point that it fails and we can get the threshold value of the function quite easily.

You can use FastCheck, QuickCheck, or Mocha Test Check for property-based testing.

Use Chai Library

Error catching should be done with specific libraries such as Chai library. It expects the assertions and hence give you what the error was about. This might not be the case with a try-catch-finally statement. A Try-Catch-Finally statement will throw some generic error statement because it takes the exceptions as a whole class of exceptions and errors and does not give the specific result about the same. It then takes lot of time to decode what the error was actually about.

For example:

expect(‘a’).to.not.have.property(‘b’);


This way, few lines of code are summarized to a single line of Chai assertion.

Check for Exceptional Scenarios

While the test cases and scenarios that you design might cover everything on the source code but there are few exceptions that are very important while testing the application behavior/response/outcome. Let say there is a feature in your application that sends email when a new user is added. The email is sent to both admin and the user. This becomes an exceptional scenario as the method must be passing correctly but you might not be getting any email. These things should be tested. The testing should also include forcefully sending different response code from the server side so that we can know how the application behaves in such a way and what values are returned. If you go to the normal approach of checking the JS conditions and test cases internally then you will be able to check the internal conditioning but will never get if your application behaves in the same way practically or not.

Many firms develop their own methods to achieve these things. A good example is Netflix, which has developed something they call Chaos Engineering, which tests their functions and methods by killing off their servers one by one. This way, they are also assured that even if one server fails, the application works correctly.

Follow the Testing Pyramid

While testing with Node.js, you should try to follow the test automation pyramid. As seen from the following image, the unit test should be taken as the base of all the testing.

Test Automation Pyramid

We do so because the unit test will cover basic units of functionality independently of one another. After the unit tests are done, then move ahead to the integration testing. Integration testing will let you test the different modules combined with one another as a group. After that, we move on to the next part of the pyramid and test the front-end or User-Interface testing using Selenium or similar tools.

As you can see, the cost incurred keeps on increasing as we move on to the pyramid but the speed keeps on decreasing. Unit test takes most of the time to execute, while the front end is tested fastest because of the less complexities and modules.

Use Component Testing

Component testing tests the functionality of the modules which are separately testable. The input/output behavior of the test object is verified by the component testing.Component Testing

As seen in the image, each component has a test plan and every test plan has various different tests under it and then they are tested to check the functionality of the component. It is recommended to use the component testing after the unit testing in the pyramid. Component testing is a very good approach and has a great coverage and speed greater than unit testing.

Keep Infrastructure Issues in Mind

More often, testers think that the testing of source code with the above practices kept in mind is the all they have to do for the proper functioning of the application. But, they are wrong. Testers tend to forget the infrastructure issues and testing them which have a great percentage of happening in the real life practical scenarios. These infrastructure issues include memory overloading and how the application behaves when it happens. Other infrastructure issues may include the sudden shutdown of the server or API becoming 50% slower which is used in the application. Infrastructure testing include testing of these issues and providing a feedback report on it so that they can efficiently managed.

Going Parallel

Parallel testing means running multiple test cases, simultaneously. Running different tests in parallel has its own advantages. If you are not following parallelism then you will run one test and provide the feedback about it then you will run another tests and provide feedback about it and so on. These feedbacks are then analyzed and are worked upon. Then, the team will check the feedback of the second test you did and then resolve them. While following parallelism, you can drastically reduce the feedback loop and provide feedback of many tests altogether which can be resolved in a lesser time as before. This way, you can save a lot of time and resources of the company. Many libraries are available for achieving parallel testing, most popular of them being Mocha and Jest.

Automate the Updates for Your Dependencies

Running the tests and following different rules require lot of libraries and different tools to work altogether in order to achieve the perfect testing. But it sometimes happens that the dependencies become out of date and other dependency require the latest version to run with each other. This creates disturbance in the smooth running of the tests and can be resolved by automating updates for your dependencies. Once you automate updates, every dependency will update by itself and will not require to manual intervention after raising the flag for the same.

Happy testing!

SignalFx is built on a massively scalable streaming architecture that applies advanced predictive analytics for real-time problem detection. With its NoSample™ distributed tracing capabilities, SignalFx reliably monitors all transactions across microservices, accurately identifying all anomalies. And through data-science-powered directed troubleshooting SignalFx guides the operator to find the root cause of issues in seconds.

Topics:
testing ,performance ,web applications ,node.js ,automation testing ,best practices ,javascript ,tests

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}