Jest Test Patterns and Mock Dependencies
While testing with Jest, follow these patterns and practices to isolate behavior and maximize the efficiency of your unit tests.
Let’s start by looking at a simple property change. Component rerendering upon a property change is asynchronous, so the order in which something is added to the DOM isn’t always predictable. We recommend that your test waits for a value change to be reflected in the DOM before checking for the expected behavior. One technique uses a Promise.resolve()
statement to check the value after the page element changes, as follows:
- Add your component to the DOM.
- Change the property value.
- Wait for the component to rerender by returning a
Promise.resolve()
value.
For more information, see the blog post Testing Lightning Web Components by Matt Goldspink at Vlocity.
To expand on our example, let’s add an attribute. Attributes let you change one value in a test to see how a change renders. Use a Object.assign()
statement to set an attribute before the appendChild
call like:
For example, let’s set the background color in our initial test to red:
Your unit testing likely involves more than just an update to a simple UI element. Some code is dependent upon the behavior of certain dependencies, such as imported modules, base Lightning components, and event handlers. But, you want your code to be in a consistent environment, free from variable behavior and timing of server calls, database requests or remote access API. The following sections include guidance on mocking behavior of these dependencies for unit testing. Mocking is a common testing practice that isolates the behavior of the code you’re testing.
Salesforce provides mock components in the sfdx-lwc-jest repo and you can find the source for the components in the lightning-stubs directory. Use these mock components for tests that include base Lightning components. The mock components match the API of the actual components but don’t have all the functionality so they serve as good resources for testing. Even though these mock components don’t fire any events, you can still invoke their event handlers.
For example, look at the miscToastNotification.js
example in our lwc-recipes repo. To simulate user input, the inputTitleEl
constant references a mock <lightning-input>
element provided by sfdx-lwc-jest. We can still query for it in the DOM and dispatch an event from it to invoke the change
handler we wired up to it. We use a mock component to maintain control over the input values since we only want to test the behavior of the notification.
Keep the following in mind as you work with our mock components.
- We recommend that your tests don’t rely on the order in which slots are rendered. For example, say that there are two named slots:
actions
andtodos
. If the expected content is present, your test passes even if the mock (or real) component swaps the order in which those slots are rendered. - Base Lightning components have some properties that aren’t reflected as attributes in the DOM. For example, in
<lightning-button>
you can set the propertyiconPosition
, which is used to determine which SLDS class to use for the button and isn't a rendered value. Design your test cases to account for this behavior, and don’t assume that all properties are reflected as attributes. - No events are fired from these mocks but you can call
dispatchEvent()
against them.
Looking at the same miscToastNotification.js
example, you can see a mock event handler, too. This test defines several constant values, then the ShowToastEventName
event triggers jest.fn()
that inserts the constants into the lightning-input
element.
At the end of the test, verify that the event handler was called as expected, and with the correct parameters.
To create references to mock components for more control over component behavior, add moduleNameMapper
settings in the jest.config.js
file. To see an example, look at how jest.config.js
in the lwc-recipes repo references some mock components with module names.
Now, let’s look at how this configuration is used in miscToastNotification.test.js
in the lwc-recipes repo. It has an import
statement:
Without the moduleNameMapper
entry, this import
statement resolves to the stub https://github.com/salesforce/sfdx-lwc-jest/blob/master/src/lightning-stubs/platformShowToastEvent/platformShowToastEvent.js.
With the moduleNameMapper
entry, this import
statement resolves to the jest-mocks custom stub https://github.com/trailheadapps/lwc-recipes/blob/master/force-app/test/jest-mocks/lightning/platformShowToastEvent.js. The mock stub has custom logic that adds other properties to the event object.
If you override the default jest.config.js
file with a custom setupFilesAfterEnv
option, merge the values with those values defined in @salesforce/sfdx-lwc-jest/config
.
In your test environment, your component likely doesn’t have access to your production schema. When you import references to namespaces, mock the return value as a placeholder during the test.
In Jest tests, we use a jest-transformer to convert the @salesforce/label
import statement into a variable declaration. The value is set to the label path. By default, myImport
is assigned a string value of c.specialLabel
. You can use jest.mock()
to provide your own value for an import. This example returns the string value set in test
instead of c.specialLabel
.
See the lwc directory of our lwc-recipes repo. Many recipes have a __tests__
directory with commented jest tests.
See Also