Unit Test Lightning Web Components with Jest

With Lightning Web Components, we not only provide a technology based on modern web standards — we’re also providing the capability to unit test them locally or within your Continuous Integration flow with one of the most popular testing frameworks for modern JavaScript – Jest. In this blog post you’ll learn best practices for writing your own unit tests.

Why unit testing matters

In an ideal world, software wouldn’t have any bugs. Till we reach that point in time (likely when Skynet dominates the world), we as developers should all agree that we have to test our code as best as we can. When you look at the four quadrants of agile testing you’ll see that unit testing is part of quadrant 1 – the first line of defense for code quality. It’s also common knowledge that both unit testing and manual testing by the developer uncovers around 2/3 of errors before they hit the next testing quadrant. The earlier you find bugs, the “cheaper” they are, because once they hit the next stages of development (or in the worst case, production), more people and processes must get involved to get bugs identified and fixed. It can ultimately lead to the loss of trust in an application.

To showcase how to write your unit tests, over the last few weeks we added a lot of tests to our LWC Recipes sample application — and we made sure that they all pass. 😉

The basics

This blog post won’t explain the setup and how to run Jest tests. That is very well documented in the Lightning Web Components Developer Guide, which you should check out. You can also consult the official Jest website to learn more about Jest in general. What you will learn in this blog post is how a Jest test is defined, what the moving parts are, and how to handle them. For that let’s look at the test for the hello Lightning web component recipe.

import { createElement } from 'lwc';
import Hello from 'c/hello';

describe('c-hello', () => {
    afterEach(() => {
        // The jsdom instance is shared across test cases in a single file so reset the DOM
        while (document.body.firstChild) {
            document.body.removeChild(document.body.firstChild);
        }
    });

    it('displays greeting', () => {
        // Create element
        const element = createElement('c-hello', {
            is: Hello
        });
        document.body.appendChild(element);

        // Verify displayed greeting
        const div = element.shadowRoot.querySelector('div');
        expect(div.textContent).toBe('Hello, World!');
    });
});

Code explanation:

  • Line 1: To create a new Lightning web component in a Jest test, we import the createElement function. This function is available only from tests.
  • Line 2: Import the Lightning web component that you want to test.
  • Line 4: A describe defines a test suite. A test suite is a block that contains one or more tests. Use a test suite to cluster tests that belong together from a functional point of view. Usually you have only one, the outer test suite, in your test file. Based on your needs, you can create more test suites.
  • Line 5: Jest offers several convenience lifecycle methods like afterEach(), where you can run setup or cleanup tasks.
  • Line 12: An it describes a single test. It should read as the description of the expected behavior. A test represents a single functional unit that you want to test. That can be either that specific UI elements are present in your Lightning web component, that a specific function is called based on a DOM event, that an @wire adapter emits the correct data when called, and so forth.
  • Lines 14-17: These lines create the Lightning web component. The document.body.appendChild() call attaches the Lightning web component to the DOM and renders it, which also means that renderedCallback() lifecycle method gets called.
  • Line 20: For querying any DOM element, you use element.shadowRoot as the parent for the query. It’s a test-only API that lets you peek through the Shadow DOM membrane to inspect a component’s internals. It’s equivalent to this.template but is accessible outside the component in tests.
  • Line 21: Jest offers a variety of matchers that let you check values and behaviors of various parts of your Lightning web component.

Now that you’ve seen an initial basic test, let’s talk about what you should focus on.

Only test the functionality of the component that you own

While this statement seems to explain the obvious, it’s something that I had to learn — sometimes the hard way. When you unit test your Lightning web component you have to focus on testing the functionality of your own component. You should not test any functionality of your child components. Whatever you expect your child components to do, that’s an implementation detail of each child component itself — and it should be covered within the tests of that child component.

Let’s look at two examples. The first HTML markup is from the recordFormDynamicContact recipe. It contains a single base Lightning component, lightning-record-form. When you look at the test for this recipe you will see that we validate if the properties (objectApiName, recordId, fields) for the base component are correctly populated. We don’t test what the base component will actually render, because that’s an implementation detail of the base component.

<div class="slds-m-around_medium">
    <lightning-record-form
        object-api-name={objectApiName}
        record-id={recordId}
        fields={fields}
    ></lightning-record-form>
</div>

The next markup is from the apiProperty recipe. This recipe sets the value of the percentage property based on user input via the lightning-input base component. The test verifies that a change in the input field actually populates the chartBar public property percentage correctly. It doesn’t verify what will happen within the chartBar component based on the property. This is covered in the test of the chartBar component itself.

<div class="slds-m-around_medium">
    <lightning-input
        label="Percentage"
        type="number"
        min="0"
        max="100"
        value={percentage}
        onchange={handlePercentageChange}
    ></lightning-input>
    <c-chart-bar percentage={percentage}></c-chart-bar>
</div>

To rinse and repeat: When you unit test your Lightning web component you have to focus on testing the functionality of the component being tested, not the functionality of its child components.

Code talks – time to explore the tests and their best practices

As mentioned before, we added a lot of tests to our LWC Recipes sample application. As every recipe focuses on one specific piece of functionality around Lightning Web Components, every test file showcases how to test it. We also added many comments to give you an easy head start to explore how they work.

There are many things to know and understand. We put together this list of best practices, recommendations, and rules that you should check out to make the best out of when crafting your own tests.

General

  • Do not mix different test cases into one single test! Really focus on writing one test for one piece of functionality. It may look like writing a lot of boilerplate, but it also forces you to focus.
  • Tests for a Lightning web component can be short or longer. Make sure you test what’s needed.
  • Describe what your test should accomplish – you’ll have better focus when writing it and others will directly see what the test is about.
  • Test that a component displays the expected outcome — be it displaying the expected UI elements, situations when no data is present, on errors, and so forth. Build a test for each of these scenarios. Bundle tests that focus on data and UI representation in test suites when it makes sense — that helps a lot with organization.
  • You may not want to — or have to — test the order of UI elements. If you do, make sure that you use modern JavaScript to optimize the code when testing multiple elements at once.

Data (Apex and @wire)

Salesforce specifics

Testing techniques

  • If you test JSON objects, use the toEqual matcher. For example, use it when you want to validate the detail parameter of a CustomEvent, and remember to mock the handler.
  • When you have to use timers in your Lightning web component, make sure to set your test up for that, and for running them before you validate the UI.
  • You may have to add CSS classes or a data-* attribute to your Lightning web components markup to get the element that you want to test. This is because the parent component owns that, not the child component. The child also owns whether to reflect a property as an attribute in the DOM.
  • Depending on what you want to test you should remember that a promise can chain more than one then().
  • You can override global APIs like fetch with your own mocks.

Check out the video walkthrough

You can find a walkthrough of creating your first Lightning web component test in our Lightning Web Components Video Gallery.

Summary

Having the capability to unit test Lightning Web Components with Jest offers developers a new level of quality assurance. As Jest is widely used, a lot of documentation and recommendations for testing with this framework is available. In an upcoming blog post we’re going to focus on more advanced tooling capabilities for your Lightning Web Components, like using Jest code coverage, or unit testing to your Continuous Integration flow.

Meanwhile, you should check out the Lightning Web Components documentation for testing, dig around what the lwc-jest tool does for you by providing mocks for all base Lightning components. And last but not least, explore the LWC Recipes tests to get started with your very first own set of tests. There is also this handy Jest cheatsheet which I highly recommend bookmarking.

About the author

René Winkelmeyer works as Principal Developer Evangelist at Salesforce. He focuses on enterprise integrations, Lightning, and all the other cool stuff that you can do with the Salesforce Platform. You can follow him on Twitter @muenzpraeger.

Leave your comments...

Unit Test Lightning Web Components with Jest