How do you performance test JavaScript code?

Category
Stack Overflow
Author
Thomas KowalskiThomas Kowalski

Just like Python and R has become the go-to programming languages for Artificial Intelligence and Machine learning, JavaScript has become the most used language for the web, especially front-end development and backend development. It enables the creation of everything from simple websites to complex applications, making it essential for developers to learn.

With this wide usage on the web, JavaScript requires robust testing to check for performance bottlenecks. Performance testing in JavaScript refers to the process of assessing and evaluating the efficiency, security, speed, and scalability of JavaScript code or JavaScript-based web applications. The primary goal is to identify any bottlenecks, performance issues, or areas of improvement to ensure optimal user experience and application functionality.

Performance testing in JavaScript is multifaceted and involves different testing strategies for different testing types.

Types of JavaScript Performance Testing

1. Stress Testing

In this type of testing, the system is put under a load that exceeds the system and the project’s expected capacity. It is used to evaluate the code behavior under these extreme conditions to identify the breaking point and observe how the code and system recover from failure. This type of performance testing involves identifying stress conditions, developing test scenarios that expose the code to extreme conditions, running the tests, and analyze the results. These performance testing creates measures for various metrics, including system limits, breakpoints, time to failure, and recovery time.

2. Load Testing

In this type of testing, the performance of the code and system are evaluated under expected load conditions, usually over an extended period of time. This will evaluate the performance of anticipated traffic and interactions to create values for various metrics like response times, throughput, error rates, and resource utilization.

3. Responsiveness Testing

This critical performance testing process aims to ensure that the application or website works correctly and efficiently across various platforms, screen sizes, orientations, and devices. It measures how fast the application responds to user interactions so as to enhance user experience. This testing creates metrics of evaluation, including load times on various devices, browsers, and screen sizes, render time, interaction delays, and visual consistency.

4. Scalability Testing

This performance testing subtype is closely related to load testing and focuses on evaluating the capability of the JavaScript application to handle growth in traffic in terms of users, data sizes, and interaction volumes. It hence, measures the application’s ability to scale up and down to handle increasing and decreasing demands.

Tools Used for Performance Testing of JavaScript Code

Each of the mentioned performance testing scenarios requires different testing tools to analyze the overall application.

1. Inbuilt JavaScript Utilities

JavaScript has built-in methods that can be used to assess the performance of your code through console logs and can even be used to write to an actual log file. The main utilities and methods include performance.now(), console.timeEnd() and console.time(). When working with a large code base, it is usually difficult to identify which parts of the code are causing performance issues. These utilities come in handy as they can measure the time it takes for specific functions or sections of code to execute and compare the results to identify improvement areas. Additionally, these utilities can be used to track the performance of your code over time, allowing you to monitor any changes in performance and identify any issues that may occur as you make changes to your code.

Consider the following code where network latency and high load are simulated to perform load, stress, and scalability testing and report the metrics using these utilities. It will report the time it takes to get results when simulating load and network latency.

function myFunction() {
    // start the timer
    console.time("myFunction");

    // do some heavy computation
    let sum = 0;
    for (let i = 0; i < 1000000; i++) {
        sum += i;
    }

    // simulate network latency
    let latency = Math.floor(Math.random() * 1000);
    let startLatency = performance.now();
    while (performance.now() - startLatency < latency) {}

    // stop the timer and log the elapsed time
    console.timeEnd("myFunction");

    // return the result
    return sum;
}

// call the function multiple times and log the average elapsed time
let totalTime = 0;
let numIterations = 10;
for (let i = 0; i < numIterations; i++) {
    let startTime = performance.now();
    myFunction();
    let endTime = performance.now();
    totalTime += endTime - startTime;

    // simulate high load
    if (i % 100 === 0) {
        let loadTime = Math.floor(Math.random() * 1000);
        let startLoad = performance.now();
        while (performance.now() - startLoad < loadTime) {}
        console.log(`Simulating high load: ${loadTime.toFixed(2)} milliseconds`);
    }
}

let averageTime = totalTime / numIterations;
console.log(`Average time when loading lots of normal data: ${averageTime.toFixed(2)} milliseconds`);

// simulate network latency
let latency = Math.floor(Math.random() * 1000);
let startLatency = performance.now();
while (performance.now() - startLatency < latency) {}
console.log(`Simulating network latency: ${latency.toFixed(2)} milliseconds`);

// call the function multiple times and log the average elapsed time with network latency
totalTime = 0;
numIterations = 10;
for (let i = 0; i < numIterations; i++) {
    let startTime = performance.now();
    myFunction();
    let endTime = performance.now();
    totalTime += endTime - startTime + latency;
}

averageTime = totalTime / numIterations;
console.log(`Average time with network latency: ${averageTime.toFixed(2)} milliseconds`);

The results from running this code are attached in the following graphic:

Inbuilt JavaScript Utilities

2. Browser Developer Tools

Most browsers today come with inbuilt Performance Testing modules for JavaScript, HTML, and CSS. Chrome DevTools, Firefox Developer Tools, and other browser-based tools allow developers to profile and debug JavaScript code, measure runtime, and identify bottlenecks in real-time. For example, the following graphics show performance testing for a blog post using Chrome Developer Tools.

Browser Developer Tools

3. Lighthouse

Closely related to DevTools is the lighthouse module, which can be accessed via Chrome DevTools, command line, WebUI, and NodeJS. Lighthouse is a free, open-source tool designed to enhance the quality of web pages by providing automated evaluations. It gets executed on any web page, whether it’s publicly accessible or requires login credentials. Lighthouse conducts assessments in several areas: performance, accessibility, progressive web apps (PWAs), and search engine optimization (SEO). Each audit has a reference doc explaining why it is important and how to fix it.

Lighthouse

Other Third-party Applications and Frameworks

JavaScript has other third-party models and frameworks that can be used for performance testing. The list is long and keeps changing as the JavaScript ecosystem evolves. Some of the most notable tools include; Jmeter, LoadRunner, Gatling, Load Views, Artillery, Jest, Mocha, and k6.

Conclusion

In conclusion, Performance Testing in Javascript can be done using various methods, including builtin methods, browser tools, and other specialized third-party tools. Though the list above is not exhaustive, it gives a base on which to build the good habit of including and using performance testing tools when writing JavaScript code.