Skip to content

4) How to write a Jest test

Coding With The Force edited this page Feb 20, 2023 · 15 revisions

Creating the Jest Test File

Before we actually write our tests, we need to make a jest test file inside of our lightning web component folder! Let's go through the steps of creating a jest test for an LWC below:

  1. If you haven't done so yet, make sure to create your LWC, we need to make it first so that we can put our jest test file in the same folder as our LWC!

  2. Once your LWC is created, create a folder named "tests" inside your LWC folder.

  3. After your folder "tests" is created inside your LWC's folder, create a new js file and name it "theNameOfYourLWC.test.js"

  4. Batta Boom, Batta Bing, you ready to make a jest test now fam.

Note: In Illuminated Cloud 2 you can just right click on your LWC's folder, go to New -> "Lightning Web Component Javascript Test File" and it will do steps 2-3 for you automatically.


The Basics of Writing a Jest Test

Depending upon the complexity of your Lightning Web Component, your jest test could be simple, or it could be complicated, but either way these basic concepts are important. Let's take a look at a very very veryyy basic jest test example and break it down piece by piece.

Simple jest test example:

describe('Addition function', ()=>{
	test('2+3=5', ()=>{
		const num = 2+3;
		expect(num).toBe(5);
	});
});

The above example is extremely simple, in fact, it's so simple it's not even testing an LWC! But you should see three key words in there. Those keywords are "describe", "test", and "expect". Let's go over each below:

describe - This keyword defines a test suite (or groups multiple tests together). Use this if you would like to group multiple similar types of tests together in a single block.

test - This keyword can also be replaced with the keyword "it", either one will work here, but I personally prefer the keyword "test" as I think it's more clear what you are writing, because what you are writing is indeed a test! The "test" keyword indicates the start of a single jest test method.

expect - This keyword is used when you want to test the value of something! For instance, we "expect" our variable named "num" "to be" the value 5 and what that expect keyword is doing above is checking to verify if that is true. It's very similar to the Assert class that you use when writing tests in Apex! If you would like to see a list of all of the "matchers" that can be appended to the end of the "expect" keyword (like "toBe" in the example above), you can check out a list of all of them here.


The beforeEach and afterEach Functions

In Apex when you want to have something setup for you prior to a test method running you would use a method annotated with @testSetup. The beforeEach and the afterEach functions in jest work in the same way. beforeEach runs code before each of your jest tests run and the afterEach funtion runs after each jest test runs. Pretty niftyyyyy. Let's check that magic out in action.

beforeEach Example:

beforeEach(()=>{
    console.log('I run before your test runs!');
});

afterEach Example:

afterEach(()=>{
    console.log('I run after your test runs!');
});

The above examples are obviously very simple examples just to give you an idea of how these methods are setup and how they work, in reality you will likely be setting up or tearing down html elements that you need for testing in your jest tests in these methods, should you decide to use them. Just as a reminder though, these methods run before or after EVERY SINGLE JEST TEST. So make sure you need the code in them running before each test and that the code won't be detrimental to any of the individual jest tests in your test js file.


Creating DOM/HTML Elements for your Jest Test

More than likely the vast majority of your LWC's have a UI component to them and the only way to properly test them would be to test that values actually were populated and displayed in the html file of your LWC. You might be thinking, "Yea, you right, but how the heck can I test that? No one is viewing the lwc during the jest test...". No worries fam, there's an answer to that in the form of "import {createElement} from 'lwc'". We're gonna use that import to build our LWC's html so we can test it properly! Yay!!

Let's take a look at a simple example of this in action:

import {createElement} from 'lwc';
import componentYouAreTesting from 'c/componentYouAreTesting';

describe('componentYouAreTesting Test Suite", ()=>{
    test('example creation of dom element', ()=>{
        const testingComponent = createElement('c-component-you-are-testing', {
            is:componentYouAreTesting
        });
        document.body.appendChild(testingComponent);
    });
});

The above code has successfully appended the lightning web component named "componentYouAreTesting" to the DOM and now you will be able to test whether or not data gets appended to that element successfully! Let's figure out how to setup a bit more complicated scenarios below, and how we can actually verify DOM elements get filled out as expected.


How to test Data Binding with Jest Tests

Data binding in your html file of your LWC is likely one of the most common things you do in all of your LWC's. In this section we're gonna figure out exactly how to test that those data binds are displaying exactly what you intended for them to test. So let's get to it!

Ok, so first things first, we need an actual LWC this time with both an html and a js file. I have a prebuilt example below. The name of the prebuilt LWC is "jest_data_bind_example" (this is important when we get to the actual jest test further down):

jest_data_bind_example JS File:

import {LightningElement} from 'lwc';

export default class JestDataBindExample extends LightningElement {
	paragraphText = "Hi I'm a Talapia";
}

jest_data_bind_example HTML File:

<template>
	<p>{paragraphText}</p>
</template>

What we're gonna do next is build a jest test that will allow us to check that the value of that bind variable is indeed "Hi I'm a Talapia". Let's check out how to do that below with a jest example:

Jest File:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';

describe('dataBindTests', ()=>{
	test('checkingParagraphText', ()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const paragraphComponent = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(paragraphComponent);
		//Finding our paragraph tag we've placed our variable in
		const paragraphText = paragraphComponent.shadowRoot.querySelector('p');
		//Verifying that we indeed have the text Hi I'm a Talapia in the paragraph tag
		expect(paragraphText.textContent).toBe("Hi I'm a Talapia");
	});
});

As you can see, this is thankfully not particularly hard to do at all! In the jest test we just create our component, place the component into the DOM (Document Object Model), find our paragraph element, and then verify that the textContent of that paragraph element is indeed what we believe it to be. Pretty nice and easy lemon squeezy, but then again, this is a very simple example, as we go on you will see things get slightly more complicated, but if you read each of these sections in order, by the time you get to the complicated stuff it'll be nbd.


How to test Events with Jest Tests

Chances are in the vast majority of your LWC's you're going to have js events incorporated somewhere, maybe a simple onchange event, or maybe a CustomEvent you made for your particular needs! In any event, we need to learn how to test those bad boiz, so let's get to it!

First things first, before we get to the jest test we need to ensure that we have a component with an actual event in it. So, let's just continue to add on to our existing component that we started building in the data binding section and give it a button that will change the value of the bind variables text on the click of the button.

LWC HTML File:

<template>
	<p>{paragraphText}</p>
	<lightning-button onclick={changePText}>Get New Paragraph Text</lightning-button>
</template>

LWC JS File:

import {LightningElement} from 'lwc';

export default class JestDataBindExample extends LightningElement {
	paragraphText = "Hi I'm a Talapia";

	changePText(){
		this.paragraphText = "Hi I'm a taco";	
	}
}

LWC Jest Test File:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';

describe('dataBindTests', ()=>{
	beforeEach(()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('testingOnClickEvent', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
		const button = lwc_jest_example_component.shadowRoot.querySelector('lightning-button');
		//triggering the onclick event on the button
		button.dispatchEvent(new CustomEvent('click'));
		//Waiting for our onclick event to finish
		return Promise.resolve().then(()=>{
			//Finding our paragraph tag we've placed our variable in
			const paragraphText = lwc_jest_example_component.shadowRoot.querySelector('p');
			//Verifying that we indeed have the text Hi I'm a Taco in the paragraph tag after the event is run
			expect(paragraphText.textContent).toBe("Hi I'm a taco");
		});
	});
});

As you can see from the above files we've now added a button with the onclick event to the LWC. The onclick will alter the text in our bind variable to the value "Hi I'm a taco". Now we just need to test it!

To do so we are finding the button in the DOM here const button = lwc_jest_example_component.shadowRoot.querySelector('lightning-button');, once we find the button we need to click it somehow to actually force the event to take place, and we do so by dispatching a custom event from it as show here button.dispatchEvent(new CustomEvent('click'));.

We have now successfully dispatched our event in our jest test! However we need to wait for our event to finish (since events occur asynchronously in JavaScript), we can do so using Promise.resolve(). After waiting for our event to finish we can now check the content of the paragraph texts bind variable to see if our event updated it successfully.


How to test Conditional Rendering with Jest Tests

Conditional rendering is pretty common in most LWC's, and making sure that an element is only rendered in the right scenario is crucial to ensuring your component works as intended (pending that's a feature of your LWC of course lol). In this section we're gonna find out how to test those scenarios, so let's check, check, check it outttt.

Just like in the previous two examples, first we need a component that actually has conditional rendering, so let's add that to the component we've been testing throughout this guide.

LWC HTML File:

<template>
	<p>{paragraphText}</p>
	<lightning-button onclick={changePText}>Get New Paragraph Text</lightning-button>
	<template lwc:if={renderImage}>
		<img src="https://coolimage.png"/>
	</template>
</template>

LWC JS File:

import {LightningElement} from 'lwc';

export default class JestDataBindExample extends LightningElement {
	paragraphText = "Hi I'm a Talapia";
	renderImage = false;

	changePText(){
		this.paragraphText = "Hi I'm a taco";	
	}
}

As you can see in the files above, we've added some a conditionally rendered image to our html

<template lwc:if={renderImage}>
    <img src="https://coolimage.png"/>
</template>

And in our JS file we have declared the renderImage variable to be false renderImage = false;. Now we've just gotta test to make sure that the element does not render when the component is in the page! Let's check out the jest test to do this now!

LWC Jest Test:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';

describe('dataBindTests', ()=>{
	beforeEach(()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('testRenderOfImage', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
		const image = lwc_jest_example_component.shadowRoot.querySelector('img');
		expect(image).toBeNull();
	});
});

The name of our new test is the "testRenderOfImage" and it's a pretty simple test huh? Basically all we're doing is loading our LWC into the page and then looking for the img element we added and expecting it toBeNull since our boolean variable "renderImage" in the JS was set to false. Of course if you were testing the reverse (boolean variable set to true) you'd want that statement to be expect(image).not.toBeNull.

Quick, simple and painless right? I told you they'd get easier the further along we got in this guide!


How to test Iterators with Jest Tests

Iterators, for:each and everything inbetween are gonna show up in tons of LWC's you make, and figuring out how to test them isn't the most obvious thing in the world. Let's go through a couple examples together on how to test iterators of every kind in your LWC's.

Let's get right to it and update our component so that it has an iterator in the html that we can actually test!

LWC HTML:

<template>
	<p>{paragraphText}</p>
	<lightning-button onclick={changePText}>Get New Paragraph Text</lightning-button>
	<template lwc:if={renderImage}>
		<img src="https://coolimage.png"/>
	</template>
	<template for:each={tacoList} for:item="taco">
		<p class="tacoInfo" key={taco.Id}>
			{taco.TacoType}
		</p>
	</template>
</template>

LWC JS

import {LightningElement} from 'lwc';

export default class JestDataBindExample extends LightningElement {
	paragraphText = "Hi I'm a Talapia";
	renderImage = false;
	tacoList = [{Id:'1', TacoType:'Chalupa'}, {Id:'2', TacoType: 'Crunchy'}];

	changePText(){
		this.paragraphText = "Hi I'm a taco";	
	

As you can see we have added the following code to our JS file tacoList = [{Id:'1', TacoType:'Chalupa'}, {Id:'2', TacoType: 'Crunchy'}];. This is the array of elements that we will be iterating over to display in our HTML file.

In our HTML file we have added the following

<template for:each={tacoList} for:item="taco">
		<p class="tacoInfo" key={taco.Id}>
			{taco.TacoType}
		</p>
	</template> 

What this does is take our "tacoList" JS variable and iterate over it to produce paragraph elements for each item in our "tacoList" array. We are going to test in our jest test that this does indeed create two paragraph elements in our component and we are going to test that the values of the paragraph elements are correct. Let's take a look at the jest test now.

LWC Jest Test:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';
const TACO_VALUES = ['Chalupa', 'Crunchy'];
describe('dataBindTests', ()=>{
	beforeEach(()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('listIterationDisplay', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
		//Creating an array/list from our content created from our iterator
		const tacoList = Array.from(lwc_jest_example_component.shadowRoot.querySelectorAll('.tacoInfo'));
		//Cleaning up our array so that it's only a list of the text content in our iterated elements
		const tacoMap = tacoList.map(p => p.textContent);
		expect(tacoMap.length).toBe(2);
		//Use toEqual to compare objects, maps or lists
		expect(tacoMap).toEqual(TACO_VALUES);
	});
});

As you can see this test is not incredibly complicated but it may be confusing if you aren't comfortable with some JS methods used within it. We are basically just grabbing all of our paragraph elements that we have created and placing them into an array in our lwc with this line const tacoList = Array.from(lwc_jest_example_component.shadowRoot.querySelectorAll('.tacoInfo'));, then we are cleaning up that array to make it less ugly and only contain the textContent of our paragraph elements here const tacoMap = tacoList.map(p => p.textContent);. After we make our clean array of our paragraph element values, we then check to ensure we only have two paragraph elements (since we only have two items in our array) and we check to ensure we have generated the right values in that array.

Pretty simple, but it does require some comfortability with JS Array methods so definitely check those out if the above code makes you a little uncomfortable.


How to test Child Components with Jest Tests

Got a component inside your component? Child components are likely a part of any quality made LWC and we absolutely need to know how to test that they are reacting right as well, so let's run through an example together of how to figure out whether your parent LWC's sweet cute little child LWC is working as expected below.

Let's take a look at our LWC with a child component added as well as the code for the child component below.

Parent LWC HTML:

<template>
	<p>{paragraphText}</p>
	<lightning-button onclick={changePText}>Get New Paragraph Text</lightning-button>
	<template lwc:if={renderImage}>
		<img src="https://coolimage.png"/>
	</template>
	<template for:each={tacoList} for:item="taco">
		<p class="tacoInfo" key={taco.Id}>
			{taco.TacoType}
		</p>
	</template>
        <!--Child component we've added-->
	<c-jest_child_comp_example turtle-info={turtleInfo}></c-jest_child_comp_example>
</template>

Parent LWC JS:

import {LightningElement} from 'lwc';

export default class JestDataBindExample extends LightningElement {
	paragraphText = "Hi I'm a Talapia";
	renderImage = false;
	tacoList = [{Id:'1', TacoType:'Chalupa'}, {Id:'2', TacoType: 'Crunchy'}];
        //JS variable we've added to pass to the child component
	turtleInfo = 'Turtles are so magical';

	changePText(){
		this.paragraphText = "Hi I'm a taco";	
	}
}

Child LWC HTML

<template>
	<h3 class='turtlePower'>{turtleInfo}</h3>
</template>

Child LWC JS

import {LightningElement, api} from 'lwc';

export default class JestChildCompExample extends LightningElement {
	@api turtleInfo;
}

If you take a look at the code above you'll notice that it's a very small and simple child component, basically all that takes place is we pass a string from the parent component to the child component on the load of the parent component. We just pass the child some sweet sweet turtle info. Nothing crazy here. If you're unfamiliar with how parent child communication works with LWC's definitely go check that out before proceeded with the jest test below.

Parent LWC Jest Test:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';

describe('dataBindTests', ()=>{
	beforeEach(()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('turtleChildComponent', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
                //Getting our child component using a query selector
		const turtleChildComponent = lwc_jest_example_component.shadowRoot.querySelector('c-jest_child_comp_example');
                const turtleHeader = turtleChildComponent.shadowRoot.querySelector('.turtlePower');
                //Checking out the value of the turtleInfo variable (which is available due to the @api tag exposing it)
		expect(turtleChildComponent.turtleInfo).toBe('Turtles are so magical');
                expect(turtleHeader.textContent).toBe('Turtles are so magical');
	});
});

I realize this is a simple example, but this should give you a good idea of how easy it is to verify a child components received data as expected and are displaying the data like you are anticipating. It's surprisingly as simple as just querying for the component lwc_jest_example_component.shadowRoot.querySelector('c-jest_child_comp_example');, assigning it to a variable and then verifying everything in just the same way we have been verifying our parent component throughout these examples. Pretty neato bandito... sorry lol.


How to test Apex Method Calls with Jest Tests

If you aren't callin Apex somewhere in your LWC, was it even worth writing? Probably not. Lol, ok, that's definitely not true, but we still need to know how to test those Apex calls within our LWC's when we make them, so let's figure out how, ok pal??

When testing Apex methods we are going to be using something a little new. We're going to use mocking, but it's a bit different from the mocking we used for wire methods because we are going to have to use jests native mocking system to produce our own mock!

Now you may be asking, why do we need to even mock this? Couldn't we just have our component call our Apex class method and have it return results to us? The answer is unfortunately no, when we run jest tests we can't run them in our Salesforce org and therefore they have no ability to gain the context of our org or use the apex classes that run within them, so we must always mock apex callouts.

So let's figure out how to do this. The first thing we're gonna take a look at is the component we will be testing and the apex class it uses below.

Apex Class:

public with sharing class Jest_Apex_Call_Example
{
        //Make sure in a real life scenario to actually use proper error handling here and ideally a selector class
	@AuraEnabled
	public static List<Case> getKewlCases(String subject){
		return [SELECT Id, Subject FROM Case WHERE Subject = :subject];
	}
}

LWC HTML File:

<template>
	<template for:each={cases} for:item="caseVal">
		<p class="caseInfo" key={caseVal.Id}>
			{caseVal.Subject}
		</p>
	</template>
</template>

LWC JS File:

import {LightningElement} from 'lwc';
import getCases from '@salesforce/apex/Jest_Apex_Call_Example.getKewlCases';

export default class JestDataBindExample extends LightningElement {
	cases;
        errors;

	connectedCallback() {
		this.getCasesToDisplay();
	}

	getCasesToDisplay(){
		getCases({'subject':'kewl case'}).then(result=>{
			this.cases = result;
		}).catch(error=>{
                        this.errors = error;
		});
	}
}

The above LWC calls out to an apex method, retrieves cases from the apex method and then uses an iterator in the HTML to display all of the cases on the page to a user. What we now need to do is figure out how to write a jest test that will test whether or not the LWC would display the cases as intended if it had successfully retrieved them from the Apex Controller. Let's take a look at how to do this.

Mock Case Data (cases.json)

[
  {"Subject":  "kewl case", "Id":  "0054R00002fDGthQAG"}
]

LWC Jest Test:

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';
import getCases from '@salesforce/apex/Jest_Apex_Call_Example.getKewlCases';

//Requiring a mock JSON object of case data (shown above)
const mockCases = require('./mockData/cases.json');

//Creating our jest mock to fake the call to our Apex Controller
jest.mock('@salesforce/apex/Jest_Apex_Call_Example.getKewlCases',
	()=>({
		default:jest.fn()
	}), {
	//Must use this virtual option parameter because we cannot load our apex class during this test run
		virtual:true
});

describe('dataBindTests', ()=>{
	beforeEach(()=>{
                //Setting the return value of our mocked apex method. We are setting it in the before each because it runs in the connectedCallback
                //of the LWC 
		getCases.mockResolvedValue(mockCases);
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('Successful call and display of cases', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
                //Finding our case values in the LWC and checking to ensure they displayed as we anticipated they would.
		const listOfCases = lwc_jest_example_component.shadowRoot.querySelectorAll('.caseInfo');
		expect(listOfCases.length).toBe(1);
		expect(listOfCases[0].textContent).toBe(mockCases[0].Subject);
	});
});

As you can see this is not crazy difficult, there's just a bunch of weird stuff going on that needs some explaining. First things first, just like in the @wire example you need to use a json file to mock your data (ideally), that's why we have created the cases.json file, and you'll store it in the EXACT same way we stored the json file for the wire example up above (so just scroll up there and reference those steps).

Second we need to create our mock method for our Apex method call and we do so using the following code:

jest.mock('@salesforce/apex/Jest_Apex_Call_Example.getKewlCases',
	()=>({
		default:jest.fn()
	}), {
	//Must use this virtual option parameter because we cannot load our apex class during this test run
		virtual:true
});

The above code tells jest when we run our tests the following:

  1. When the method '@salesforce/apex/Jest_Apex_Call_Example.getKewlCases' is called in our code we need to run a fake method instead of the real thing. We do this by using the jest.mock('@salesforce/apex/Jest_Apex_Call_Example.getKewlCases')
  2. The default:jest.fn() is telling jest to use a mocked method call for the default export of the node module. This is confusing, but just know that when importing an apex method call to your LWC, it uses the keyword default to export that apex class to you (If you'd like to learn more about default modules can do so here).
  3. We are telling this mock that it is a virual mock using this line virtual:true and we do this because there is no way to truly import our apex module into this jest test for use due to the fact that we can't run jest tests inside our Salesforce org. If we don't use this option the jest test will fail because it is expecting us to provide it with a js module that it can see and run.

After we setup this jest mock method the only thing we need to do is tell it what data it should return, and we do so with this line getCases.mockResolvedValue(mockCases);. This informs jest that whenever the apex class is called it should successfully return our mockCases.json for the LWC to use.

And that's it! After that nothing is really out of the ordinary, however there are two other things you should know exist when you are mocking Apex Class callouts:

  1. There is also a method called mockRejectedValue that you should use in place of mockResolvedValue if you would like to test error/exception scenarios. You will also pass this some JSON you expect it to receive.

  2. If you are testing an Apex call from an event (like a button click or something) you need to make sure you wait for that event to resolve before testing whether or not the page was updated successfully and unlike all of the above examples where we use Promise.resolve() we instead should use return new Promise(setImmediate).then(()=>{//test code here}). This is due to how the order of operations work in JS for Promises. Promise.resolve() will not occur in at the right point in time to test, whereas Promise(setImmediate) will. Also, if you use setImmediate, make sure to import the following into your jest test at import {setImmediate} from 'timers';, otherwise it may complain that setImmediate doesn't exist.

If you're interested in learning more about the timing of Promises you can do so here


How to test @wire with Jest Tests

You, me, and your best friend Jean Ralphio have all got @wire methods in our LWC's whether we like it or not, let's figure out how to test those cutie pies below.

Testing @wire calls is gonna require our first introduction to mocking data for our jest tests (well kinda, we've also mocked less complicated things up above with the iterators). The way to go about mocking data for our jest tests is to create a subfolder inside of our "test" folder and create json files within that new subfolder. I personally typically name that subfolder "mockData", but you can call it whatever you want. We'll come back to this in just a moment, but feel free to create that subfolder ahead of time if you'd like.

Let's take a look at a simple LWC that uses a @wire for a call to an apex method before we get into the jest test.

Apex Class:

public with sharing class Jest_Wire_Example{
	@AuraEnabled(cacheable=true)
	public static List<Account> getAcctList(){
		return [SELECT Id, Name FROM Account WHERE Name = 'Matts Account'];
	}
}

LWC JS File:

import {LightningElement, wire} from 'lwc';
import getAccounts from '@salesforce/apex/Jest_Wire_Example.getAcctList';

export default class JestDataBindExample extends LightningElement {
	@wire(getAccounts)
	accounts;
}

LWC HTML File:

<template>
	<template for:each={accounts.data} for:item="account">
		<p class="acctInfo" key={account.Id}>
			{account.Name}
		</p>
	</template>
</template>

As you can maybe gather from the above component files, what we are essentially doing is querying for accounts in our apex controller, retrieving them via a @wire call, storing them in the accounts variable in our JS file and then displaying them using a for:each iterator in our HTML. Nothing too wild here, however, there is no way for us to actually query the Accounts object in Salesforce when running our jest tests, because our jest tests don't run inside our Salesforce org, they run in a little world of their own. So how will we will actually be able to test this?? As mentioned above, we will be mocking our data! If you haven't made that folder mentioned above, please do so now! Once you have made that folder, create a JSON file within it. You can name this JSON file whatever you want, but for this example I have named it "wireMockData". Let's take a look at the "wireMockData" JSON together now.

**wireMockData JSON: **

[
  {"Name":  "Matts Account", "Id":  "0014R00002fDGthQAG"},
  {"Name":  "Matts Account", "Id":  "0014R00002fDGtdFAL"}
]

As you can see it's just a JSON representation of a couple accounts. You'll wanna find the exact JSON structure of your @wire data to put in your wireMockData file, which you can do by using a console.log in your LWC like so console.log(JSON.stringify(this.accounts.data)). Now that our JSON file is built, let's check out the jest test and see how we can actually use this mock data to test our @wire call.

**LWC Jest Test File: **

import {createElement} from 'lwc';
import jestDataBindExample from 'c/jest_data_bind_example';
//Importing this to indicate which fake wire call we should make
import getAccounts from '@salesforce/apex/Jest_Wire_Example.getAcctList';
//Importing our mockData JSON file
const mockAccounts = require('./mockData/wireMockData.json');

jest.mock("@salesforce/apex/Jest_Wire_Example.getAcctList",
	()=>{
		const {createApexTestWireAdapter} = require("@salesforce/sfdx-lwc-jest");
		return{
			default: createApexTestWireAdapter(jest.fn())
		};
	},
{virtual:true});

describe('dataBindTests', ()=>{
	beforeEach(()=>{
		//Creating the jest_data_bind_example using the createElement command, so that we can test it
		const lwc_jest_example_component = createElement('c-jest_data_bind_example', {
			//verifying we made the correct element
			is:jestDataBindExample
		});
		//Appending the jest_data_bind_example component to the document/DOM
		document.body.appendChild(lwc_jest_example_component);
	});

	test('wire service data retrieval', ()=>{
		const lwc_jest_example_component = document.querySelector('c-jest_data_bind_example');
		//mocking our the call to our wire service using the emit method
		getAccounts.emit(mockAccounts);
                //Using Promise.resolve() to wait until our wire call is finished executing before continuing our test
		return Promise.resolve().then(() =>{
			const wireIterationArray = lwc_jest_example_component.shadowRoot.querySelectorAll('.acctInfo');
			expect(wireIterationArray.length).toBe(2);
			expect(wireIterationArray[0].textContent).toBe(mockAccounts[0].Name);
		});
	});
});

So, as you can see above, things get a bit more complicated here, but nothing super crazy. Let's break down what we're doing:

  1. We're creating a mock of our apex wire method (much like we did in a regular apex method call)
  2. We're importing the "getAccounts" apex method so we can use it to emit our mocked data in the test method
  3. We're importing/requiring our mockDataJSON using this line here const mockAccounts = require('./mockData/wireMockData.json'); so that we can use it later to send records to our LWC in the jest test.
  4. In our jest test we are using this line getAccounts.emit(mockAccounts); to force our LWC to call the wire method and have it return our mockAccounts to the component to display in our iterator.

That's it, pretty simple once you give it a try, but very confusing when you take your first swing at it. Give it a shot, you'll likely find it's easier than you anticipated!


How to test LMS (Lightning Message Service) with Jest Tests

If you haven't used LMS yet, honestly you are missin out. It's one of the more complicated things to test, but it's not crazy hard, we'll just have to use more of that stubbing we found out about when testing apex class calls! Let's check check check it outttttt.


How to Test Navigation Mixins with Jest Tests

Navigation Mixins are frequently used in LWC's yet there is unfortunately no simple way to mock them, so let's figure out how to suffer through nav mixin mocking together lol.