Unit testing: The Beginner's guide

ozor

Ozoemena

Posted on October 9, 2023

Unit testing: The Beginner's guide

Unit Testing: Beginners guide

Introduction

Think about it, how is it that builders are able to erect a tall, straight wall? By checking each block laid with a plumb line to make sure they are on a straight line. That is what every developer does to each function, method and code to make sure they give the expected output.
JavaScript unit testing is a JavaScript testing framework built by Facebook primarily to test React-based apps. It can actually be used to test any JavaScript codebase.
In this tutorial, you will be learning
• What unit testing is all about
• Steps to unit testing
• Properties of a good unit testing
• Advantages and Disadvantages of unit testing
• Installation of jest
• How to write a Simple unit testing
• Matchers
• How to test for numbers, String, object, array and truthy

What is unit testing

Unit testing is one of the early precautions taken during the software development process. It is a process in which the smallest, testable part of an application is scrutinized to see if it works as intended. It involves the isolation of code such as function or method into small independent units and testing them individually. The idea is to pick these functions and methods and isolate them by replacing the external dependencies like the network connection and database with dummy objects.

Steps to unit testing

As a developer, you might not be able to test every line of code as this will waste a lot of your time, it is important to focus on codes that are critical to the performance of your application.
Unit testing is a component of test-driven development (TDD). TDD is a methodology that involves three critical activities that are interwoven. They include unit testing, coding and design (in the form of refactoring of code). TDD is all about Red/Green testing. This means that we would write the test, fail the test, and then finally write the code to pass the test.

Properties of a good unit test

  1. Small and focused: testing a single unit of code makes it easier to debug and locate the source of the problem
  2. Write the test before the code. This makes it easier for developers to think about the functionality and clarity of code. It makes it easy to write testable code.
  3. Use of descriptive test names. This is important as it helps developers to understand what the test code is meant to do.
  4. Use assertion (expressions containing testable codes) to ensure that the code output matches the desired behaviour.
  5. Test edge cases such as boundary condition, null, empty input and other less common scenarios that could still occur.
  6. Run tests regularly
  7. Refactor the codes and make sure they test the intended functionality

Advantages of unit testing

a. It helps in the early detection of bugs
b. It makes it easier for a developer to change the code base
c. It makes fixing errors cheaper
d. Improves code quality
e. Faster development cycle as it reduces the time for debugging

Disadvantages of unit testing

  1. It won’t uncover every bug
  2. It won’t catch errors in integration with other data sets
  3. Multiple lines of test codes need to be written to test one line of code
  4. It has a steep learning curve

Installation of Jest

We will look at how to write a simple unit test in JavaScript using Jest.
To do this you need to make sure you already have node and vscode installed in your computer.
Open your Vscode terminal and type the following command
a. npm init –y this helps you to install the node module dependencies and the package.json file
b. npm i –D jest this installs the jest
c. In your package.json file add the following section

{
    “script”: {
               “test”: “jest”
         }
}
Enter fullscreen mode Exit fullscreen mode

d. Your package.json file should look like this

{
  "name": "jest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^29.7.0"
  },
}
Enter fullscreen mode Exit fullscreen mode

How to write a simple Jest Testing

  • Create a file named function.js. This is where you write the function you want to test. In this case, the name of the function is “add”. You will notice that it is isolated and packaged inside an object called functions. This function object is then exported to the file where it is going to be tested
const functions = {
    add: (num1, num2) => num1 + num2,
}
module.exports = functions
Enter fullscreen mode Exit fullscreen mode
  • Import the function into the test function, function.test.js
 const functions = require('./function')

test('Adds 2 + 2 to equal 4', () => {
  expect(functions.add(2,2)).toBe(4);
});

Enter fullscreen mode Exit fullscreen mode

In line 1, the code is imported from the function file.
In the next line of code, the test method is used to test the function imported from the function file.
The test method takes two parameters, the first parameter is a string and gives us a description of what is being tested. The second parameter is a function that uses the expect method to test the add function. In this case, the add function accepts the required parameters, 2 and 2. This is then compared to the output which is 4 using the toBe method.

  • At the terminal, type "npm test" to test the code if it passed or failed.

We might want to just include the function in the test function rather than importing it from an outside file. For Example

test('Should be under 1600', () => {
    const load1 = 800;
    const load2 = 700;
    expect(load1 + load2).toBeLessThan(1600)
})
Enter fullscreen mode Exit fullscreen mode

Matchers

In jest, matchers are used to test values in different ways. There are different types of matchers depending on the kind of value you want to test.

Equality Matcher

In the previous examples when the code expect(function add(2 + 2) returns an expectation, you will need to add a matcher .toBe(4) to them in order to ascertain if the outcome matches. toBe matcher test for an exact equality of values using the Object.is

test('Adds 2 + 2 to equal 4', () => {
  expect(functions.add(2,2)).toBe(4);
});
Enter fullscreen mode Exit fullscreen mode

We can also test the opposite of a matcher using the not. Take a look at the following test code.

test('addition of positive numbers not equal to zero', () => {
    for(let i = 1; i < 10; i++){
        for(let j = 1; j < 10; j++){
            expect(i + j).not.toBe(0)
        }
    }
})
Enter fullscreen mode Exit fullscreen mode

In this test code, we are testing a set of two numbers sum together using nested for loop. We want to find out the chances that the sum of any two numbers is not equal to zero

Floating Number Matcher

When testing for floating numbers, we don’t use .toBe matcher as it will fail because of rounding error. We rather use .toBeCloseTo matcher. For example

test('adding floating point number', () => {
    const value = 0.53 + 0.23;
    expect(value).toBeCloseTo(0.76)
})
Enter fullscreen mode Exit fullscreen mode

Array and Object Matcher

While the .toBe matcher is for primitive number values, we can decide to test for arrays and objects using the .toEqual matcher. This compares object and array recursively (repetitively) by checking every field in the object and array.

test('comparing object', () => {
    const detail = {
       name: "Franklin",
       age: 14
    }
    detail["gender"] = "male"
    expect(detail).toEqual({name: "Franklin", age: 14, gender: "male"})
})
Enter fullscreen mode Exit fullscreen mode

We can check if an iterable such as an array contains an item using the toContain matcher. toContain matcher actually uses the (===) strict equality to check if an array contains an item. It can also be used to check if a string is a substring of another string.

test("check if 'Mazda' is in the list of cars", () => {
    const carList = [
        "Toyota",
        "Volvo",
        "Mazda",
        "Honda",
        "Kia"
    ]
    expect(carList).toContain("Mazda")
})
Enter fullscreen mode Exit fullscreen mode

Number Matcher

We can also test for numbers using the equivalent matcher. For example

test('testing for numbers using equivalent matcher', () => {
    const value = 2 + 2
    expect(value).toBe(4)
    expect(value).toEqual(4)
    expect(value).toBeGreaterThan(3)
    expect(value).toBeGreaterThanOrEqual(3.5)
    expect(value).toBeLessThan(5)
    expect(value).toBeLessThanOrEqual(4.5)
})
Enter fullscreen mode Exit fullscreen mode

From the above test code, we can see that it is possible to use more than one expect code to test for numbers in a test code.

String Matcher

We can test for a string using a .toMatch matcher and a regular expression

test("there is no V in psychology", () => {
    expect("psychology").not.toMatch(/V/)
})
test("check for 'urc' in Church", () => {
    expect('Church').toMatch(/urc/)
})
Enter fullscreen mode Exit fullscreen mode

Truthy Matcher

We can test for truthy and falsy values using:
toBeNull which matches only Null
toBeUndefined which matches only undefined
toBeDefined which is the opposite of toBeUndefined
toBeTruthy which matches anything an if statement treats as true
toBeFalsy which matches anything an if statement treats as false

Information at the Terminal: When the test passed

When we run the test and it passes, we are expecting to see the following information in our terminal. From the information provided at the terminal, we can see that there are 12 tests carried out and they all passed. At the end of each of them, you can also see the amount of time taken for the test to be executed. As a programmer, it is important we try to write codes that get executed in the shortest possible time as it will mean that our application will run fast.

Image description

From the image above you can see that the statements underlined in yellow are the string in the test function which describes what we are testing, while the time taken to execute is circled in red. The part circled with a blue marker gives us a summary of the whole test carried out. The total number of tests was 12 and the total time spent on all the tests was 0.838 sec. If you look closely you will notice that one of the tests has the highest time of execution, 17 ms. This is because it is a test for a nested for loop and had the highest time of execution.

When the test failed

If we take a look at the summary of the test, we will see that there are 12 tests carried out in total but only 11 passed the test. The one that did not pass is also shown in red mark. The description of the failed test is also shown in red. It goes further to indicate what is expected as against what it received from the input. This information helps us to input the right information so as to pass the test.

Image description

Conclusion

In this tutorial, we have been able to examine what unit testing is all about, how to install jest, how to write, the different matchers and finally a brief interpretation of the information at the terminal.

💖 💪 🙅 🚩
ozor
Ozoemena

Posted on October 9, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

What was your win this week?
weeklyretro What was your win this week?

November 29, 2024

Where GitOps Meets ClickOps
devops Where GitOps Meets ClickOps

November 29, 2024