CodeLive: Jest Testing with Heather Dykstra

Last week on CodeLive Heather Dykstra joined me to work on some Jest tests, as our whole team has been focusing on adding them for our sample apps. Jest is an industry leading Javascript testing framework and compatible with Lightning Web Components. In our CodeLive episode (watch the episode here) Heather and I focused on writing some Jest tests for the listInfiniteScroll component from the Visualforce to LWC sample app. Along the way, we learned a few things I wanted to draw attention to!

Highlights from the show

The first thing we worked out was: what to test? There’s a number of ways to do this, but we settled on a combination of interacting with the component and looking at its template. Since neither of us wrote this component, interacting with it in an org helped us understand just what the component was supposed to do! But to identify our test cases we looked at the template. For example:

<template>
    <template if:true={records}>
        <div class="table-container">
            <lightning-datatable
                key-field="id"
                data={records}
                columns={columns}
                enable-infinite-loading
                onloadmore={loadMoreData}
            >
            </lightning-datatable>
        </div>
    </template>
    <template if:true={error}>
        <c-error-panel errors={error}></c-error-panel>
    </template>
</template>

Looking at this template, we identified two if:true blocks which gave us logical test cases for when records is true, when records is false and when error is true. We also decided we needed a test demonstrating the onloadmore handler being called.

We also quickly realized that we could execute our tests faster than Jest could update the testing DOM with components. This led us to utilize a Promise to ensure our DOM rendered any components before we asserted against them. Throughout the Jest test codebase you’ll often see code similar to this:

return Promise.resolve().then(() => {
    const errorPanelEl = element.shadowRoot.querySelector(
        'c-error-panel'
    );
    expect(errorPanelEl).not.toBeNull();
});

This code wraps our query selector call, and any expect-assertions in the promise’ then clause. This is conceptually similar to the way Test.stopTest() works. Like Test.StopTest(), this pattern forces the test to wait until the async dom manipulation has completed. If you’re not familiar with promises in Javascript, check out this excellent Trailhead module: Modern Javascript Development.

For our last test, we struggled with getting our test to fire the onloadmore event. About 5 minutes after the stream ended, we got Philippe Ozil on the phone and he sorted us out. Turns out Heather and I just need to write more Jest tests, because it was a simple naming convention tripping us up. We tried using this code:

dataTableEl.dispatchEvent(new CustomEvent('onloadmore')); //doesn't work 🙁

This, sadly, doesn’t work. The naming convention for events prepends ‘on’ for event handlers, not the event names themselves. Simply removing ‘on’ from our custom event solved our testing problems! Remember to emit events without the ‘on’ prefix! For example:

new CustomEvent('loadmore')

These are just a few of the things we learned writing LWC tests in jest. We had a blast, and I highly recommend writing your own Jest tests to get a feel for how they work. Not sure where to get started? Well, there’s a ‘module for that!The recently released Lightning Web Component Tests is an excellent place to start. You can also watch Heather and I with the on demand episode here.