Writing unit-testable JavaScript code

kamesh sethupathi
3 min readOct 19, 2021

A technical investment that saves much of your time in the latter part

Why Unit testing?

  • Consider a scenario where you are using a library named AxImage to manipulate image files in your application. Now due to performance reasons, your team internally developed another image manipulation library named BxImage.
  • Now you have to migrate from AxImage to BxImage. The following problem arises,

Have I properly migrated the library?

Is there any bugs in the newly migrated library?

Will anything break?

Can I move the code to productions confidently?

  • Consider another scenario, where you modified a key utility function in your codebase. The same questions arise for you here too like the code migration case.
  • Finding the error in the integrated module by manually testing all functionalities is much more complicated.
  • Here comes the unit testing concept to rescue you! Unit testing ensures that your change does not break anything and is working as per your expectation.

What is unit testing?

  • The goal of unit testing is to segregate each part of the program and test that the individual parts are working correctly as you expected.
  • You simply can’t overlook the benefits of tearing your application apart, testing each, and putting it all back together.

Benefits of unit testing

The key benefits of unit testing are,

  • Helps to reduce the cost of bug fixes.
  • Allows refactoring code confidently.
  • Improves the quality of the code.
  • Simplifies the debugging process.
  • Provides confidence to move your code to production.

What makes your code testable?

  • Keep Business logic and UI separate:
// hard to test
$('#cta').on('click', () => {
$.get('/path/to/data').then(data => {
$('#target').html('items: ' + data.join(', '));
});
});
// Testable code
function fetchData(onSuccess) {
$.get('/path/to/data').then(onSuccess);
}
function renderData(data) {
$('#target').html('items: ' + data.join(', '));
}
$('#cta').on('click', () => fetchData(renderData);
  • Use Dependency Injection: Pass external dependencies as arguments.
// Hard to test
function handleLogin(name, pass) {
window.auth.login(name, pass);
}
// Testable
function handleLogin(name, pass, authHandler) {
authHandler.login(name, pass);
}
  • Write cleaner functions: A function should do only one purpose that is intended for.
// Not unit testable
// Here its intended function is to add number but it does other thing also.
function add(a, b) {
let isString = (c)=> typeof c == 'string';
a = isString(a)? a.length : a;
b = isString(b)? b.length : b;
return a + b;
}
add(ipt1, ipt2);// Below is unit testable
function add(a, b) {
return a + b;
}
function isString(c) {
return typeof c == 'string';
}
function myLogic(a) {
reutn isString(a)? a.length : a;
}
add( myLogic(ipt1), myLogic(ipt2) );
  • Never mutate passed arguments: In JavaScript, arrays and objects are passed by reference rather than value, and they are mutable. Should be treated with care. None of the functions of your code calls are altering your objects.
// wrong
function addModified(userObj) {
userObj.modified = new Date();
return userObj;
}
// correct
function addModified(userObj) {
return {
...userObj,
modified: new Date()
};
}
  • Better have a wrapper around the third-party libraries: Instead of using third-party libraries directly, using around with wrappers allows easy code refactoring and unit-testable codebase.

My opinion.

  • Writing testable code is a time-consuming task, but there come a lot of benefits to it.
  • Unit testing is a technical investment that saves much of your time in the latter part. You will deliver software with better quality.
  • LinkedIn : Kamesh Sethupathi

--

--