• 9 minute read
  • Jest: The Fast and Effective Framework for Unit Testing

    An easy to follow guide on performing unit testing for ReactJS using Jest framework, and with how it helps us move a lot faster at Decentro.

    Unit testing is a crucial part of the software development process. It allows developers to verify that individual code units are working as expected. 

    This blog post will discuss how to perform unit testing in a ReactJS application using the Jest testing framework.

    Some critical aspects of Unit testing for ReactJS:

    1. The chances of code failure are less. It prevents unexpected errors even at the time of production
    2. It allows developers to focus on their current task rather than worrying about the previous task.
    3. It reduces the need for manual testing.

    Well, now we know why we need unit testing, let’s look at what the market offers in terms of great durability, stability, and performance.

    Players in the unit testing market
    Players in the unit testing market
    • Enzyme:

    Enzyme is a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output.

    Enzyme, created by Airbnb, adds some great utility methods for rendering a component (or multiple components), finding elements, and interacting with elements.

    It must be installed in addition to tools already bundled with CRA.

    Enzyme has many more traversal and interactive methods (including first and setProps).

    • Cypress

    Cypress is a free and open-source automation tool, MIT-licensed and written in JavaScript. As of this writing, it has over 19.3K Stars on Github and is used by organizations such as NASA and DHL. With the help of Cypress End to End test, integration and unit tests are easy to write and debug. 

    Cypress is built and optimized as a tool for local development. Cypress also offers a visual interface to indicate what all tests and which all commands are running, passed, or failed. It allows us to test highly interactive applications and carry out different tests, such as 

    • Manipulating the DOM
    • Asserting that some element is available or present on the screen, 
    • Reading or writing data into/from fields
    • Submitting forms, and 
    • Redirection to a different page without actually making direct modifications to your code.  

    In fact, after using Cypress for some time, you may be tempted to do all of your development within it since it provides a platform to debug and maintain your code easily and quickly.

    • Mocha

    Mocha is a feature-rich JavaScript test framework running on node.js and 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.

    • Jest 

    And finally, the crowd favorite, Jest.

    Unit testing in ReactJS is typically done using a combination of the React Test Renderer and the Jest testing framework. The React Test Renderer library allows developers to render React components and perform assertions on their output.

    Now, Wait a minute, is it that Fast ?!

    Well yes!

    Like a 500-horsepower car, Jest has out-of-the-box features that can be integrated immediately. It is as simple as creating a file with the suffix “.spec.js” file.

    Let’s break down exactly how the Jest framework has an advantage over the other tools that we have in the market for unit testing React Js applications. Real speed is determined by Performance, Snapshot Testing, and finally, Test Isolation and sandboxing.

    Here is how Jest is a superior tool in the given three parameter

    Performance: (Engine)

    Jest is made for scale to support both vast and little projects. It’ll always be fast because it runs tests in parallel. It’s simply not helpful to attend for a code base to be built before tests are often run and not scalable for more significant projects.

    Jest can execute the tests and provide feedback on whether they passed or failed. If any tests fail, Jest will provide detailed information about the failure, including the expected and actual values, so you can troubleshoot and fix the issue.

    In a nut-shell unit, testing is an integral part of the software development process, and By writing and running tests using Jest, you can ensure that your ReactJS components are working as expected, helping to prevent bugs and other issues in your application

    Now that we know what’s under this powerful Framework jest, let’s look into how we can use it and read the user’s manual.

    Snapshot Testing (Brakes)

    Yep take a snap, and voila, your test cases are done. 

    As oversimplified, the statement made above looks that is the basic idea behind it.

    Jest supports snapshot testing, which can be handy for preventing accidental UI regressions when doing React development. These tests record snapshots of rendered component structure and compare them to future renderings. Your test fails when they don’t match, indicating that something has changed. You can quickly tell Jest to update the snapshot if this change is expected (e.g., for a newly added feature).

    A similar approach can be taken when testing your React components. Instead of rendering the graphical UI, which would require building the entire app, you can use a test renderer to quickly generate a serializable value for your React tree

    Best practices to make snapshots easier
    Best practices to make snapshots easier

    There are a few basic practices that make creating snapshots easier

    • Treat snapshots as code
    • Tests should be deterministic
    • Use descriptive snapshot names

    Test isolation and sandboxing: (Transmission)

    No two tests will ever conflict with one another, nor will there ever be any global or module local state that’s getting to cause trouble.

    So let’s say we have three components in the react tree, and we are creating two to three test cases for each element that may create conflict, but in jest, as we have test isolation and also every test file is sandboxed, the execution of these test cases are uniquely handled, and the interference of two cases is not likely to happen

    How to drive Jest Fast! (Users manual)

    To write unit tests in Jest, you will need to have a basic understanding of the following concepts:

    Basics of unit tests in Jest
    Basics of unit tests in Jest
    • Test case: A test case is a single scenario you want to test. For example, you might have a test case that checks if a user can log in to your application.
    • Test suite: A test suite is a collection of test cases grouped because they test the same functionality or feature.
    • Assertion: An assertion is a statement that checks whether the output of a particular piece of code matches the expected result. Jest provides many built-in assertions that you can use to make assertions about the values of your code.
    • Mocking: Mocking replaces a function or module with a fake implementation used to control the code’s behavior under test. This can be useful when testing how a code reacts to different inputs or scenarios.

    To start unit testing in Jest, you first need to install the jest npm package. You can do this by running the following command:

    npm install --save-dev jest

    Once Jest is installed, you can create a test file for your React component by adding a .test.js file in the same directory as your component. For example, if your component is called MyComponent, you might create a test file called MyComponent.test.js.

    To write a test case, you will need to use the test function provided by Jest. This function takes two arguments: a string that describes the test case and a callback function that contains the code for the test case. 

    For example, the following code shows a simple test case that checks if a user can log in to your application:

    test('User can login', () => {
      // Code for the test case goes here
    });

    Inside the callback function, you can use Jest’s built-in assertions to make assertions about the values of your code. For example, the following code shows how you might assert that a user’s name is displayed after they have logged in:

    test('User can login', () => {
      // Code for logging in the user goes here
    
      // Assert that the user's name is displayed
      expect(user.name).toBe('Decentro');
    });

    In this example, the expected function is used to assert the value of the user.name property. The toBe function is used to check if the value of user.name is equal to the string ‘Decentro’. 

    If the value of user.name is not equal to this string, the test case will fail. You can use Jest’s mocking capabilities to control the behavior of your code under test. For example, the following code shows how you might mock a function called login that is used for logging a user in

    test('User can login', () => {
      // Mock the login function
      const login = jest.fn();
    
      // Code for logging in the user goes here
    
      // Assert that the login function was called
      expect(login).toHaveBeenCalled();
    });

    There are functions that allow you to test different aspects of the component, such as whether it renders correctly or whether it responds to user input as expected. For example, the following code shows how to test a simple ReactJS component that displays a message:

    import React from 'react';
    import { shallow } from 'enzyme';
    import MyComponent from './MyComponent';
    
    describe('MyComponent', () => {
      it('renders the correct message', () => {
        const wrapper = shallow(<MyComponent />);
        const message = wrapper.find('.message');
        expect(message.text()).toBe('Hello, Decentro!');
      });
    });

    In this example, we are using Jest’s description and it functions to organize our tests. The describe function groups together related tests, while it function specifies a single test. In this case, the test checks that the component renders the correct message by using Jest expect a function to compare the actual message with the expected message.

    Once you have written your tests, you can run them using the following command:

    npm test 

    This test imports the MyComponent component and renders the Shallow method from the enzyme library. The shallow method allows us to render the component without rendering any of its children. This is useful for isolating the component and ensuring that our test only covers its behavior and not the behavior of any child components.

    Once the component has been rendered, we can use the expected function from Jest to make assertions about its output. In this case, we are using the

    toMatchSnapshot

    method that will take a snapshot of the component’s output and save it to a file. This snapshot can be used as a reference for future tests, allowing us to verify that the component’s output has not changed unexpectedly.

    To run your tests, you can use the

    npm test

    command. This will execute all of the tests in your project and report on their results. 

    You can also use the

    --watch

    flag to automatically run your tests whenever a file in your project is changed.

    In addition to the shallow method, the enzyme library provides several other practical methods for rendering and testing React components. For example, the mount method will render the component and all of its children, allowing you to test the component in a more realistic environment. On the other hand, the render method will return an HTML string representation of the component, which can be helpful for testing components that generate HTML output.

    Testing a login component(Test Drive!)

    After reading the user’s manual and understanding the basics let’s put this knowledge to use. 

    The scenario? To test a login component that has two input fields and two buttons.

    Test cases 

    • To check whether we have two buttons
    • To check whether the input type of the password field is the password
    • To check whether the email verification takes place 

    Let’s get coding 

    1. To check whether we have two buttons submit and reset 
    import {render,screen} from "@testing-library/react"
    import {Login,validate} from "Login"
    describe("Login component test",async()⇒{
    test( 'render two buttons on the login page ',()⇒{
    render(<Login/>)
    const buttons = await screen.findAllByRole('button')
    expect(buttons).toHaveLength(2)
    })
    1. To check whether the input type of the password field is the password
    test('password input should be of type password ',async()⇒{
    render(<Login/>)
    const password = screen.getByPlaceholderText("password")
    expect(password).toHaveAttribute('type','password')
    })
    1. To check whether the email verification takes place 
    test('email validation function to be tested ',async()⇒{
    render(<Login/>)
    const testmail = 'decentro.com'
    expect(validate(testmail).not.toBe(true))
    })

    Let’s look at the code and see the results for the test cases 

    In the above image, you can see that the test suite of testing the login component has passed and the test cases have also passed

    Let’s say we enter the right email address in the test email variable as decentro@gmail.com. This scenario should fail the test case as we have set it not to be true.

    There you have it! A pretty smooth test drive.

    How Jest helped Decentro cross the finish line.

    Decentro’s ethos lies in building and making use of reusable components. These components have a lot of use cases that require them to have a different set of inputs, which render the desired outputs.

    The best way to test them is to make use of the toMatchSnapshot method which is provided by Jest. The ability to test out the simulated outputs for the specific components for the particular use case furnishes the following advantages 

    Advantages of Jest for Decentro
    Advantages of Jest for Decentro
    • It gives us a good overview of the component interaction 
    • UX flows can be designed in a specific way when we know the use cases of the components 
    • Development can be accelerated and fed into the feature rollout funnel.

    The ability to use cases and the components accordingly allows the Decentro team to fulfill client requirements in an accelerated yet precise timeline.

    Conclusion (Finish Line!) 

    In conclusion, unit testing is an integral part of the software development process, and ReactJS and Jest provide a powerful combination of tools for performing unit tests. By using the React Test Renderer and the enzyme library, you can quickly render and test your React components and use the Jest testing framework to make assertions about their output.

    It is a match made in speed heaven.

    With that, we have reached the end of an immersive read elucidating the world of unit testing. The developers at Decentro, incessantly work on getting their subject matter expertise to you via these technical write-ups. Feel free to check out our workaround, Data Tables, and Next, and leave suggestions of what you wish to read next.

    In case you wish to connect with us, feel free to drop us a line at hello@decentro.tech