Testing Lightning Web Components

Testing Lightning Web Components

At Vlocity, Matt Goldspink focuses on developing beautiful user interfaces using Salesforce’s Lightning technology, and with Salesforce's announcement of exciting changes to the platform, web development in general just got even more interesting.

 

by Matt Goldspink, Lead Engineer & UI Architect at Vlocity

For anyone with experience of Aura component development, they’ve probably faced the issue of how to test the things! They were so heavily tied to the platform that they couldn’t easily be tested in isolation.

Fortunately, Lightning Web Components has true TDD built in from the outset. It’s super simple to write tests and make assertions. Even better, if you’re used to writing tests with Jasmine, Mocha or Jest then you’ll be right at home.

Setting up your Project

If you don’t yet have an LWC project I’d suggest following my previous post. In this post we’ll take what we did in that project and evolve to add some simple unit tests that we can run right from the command line on your computer… no need to upload code to the org!

Firstly we’ll need to add a project.json file to the root of our project. If you don’t have a project.json, just run npm init and follow the walkthrough – most options can be defaulted. After running this command you’ll need to add our test dependencies. On the command line run:

Image 1

This will install Jest (a popular testing framework from Facebook used heavily on React) and Salesforce LWC Jest integrations, including the compiler and engine. Now open your package.json and under the "scripts" object add a "test:unit" property who’s value is simply "jest".

Image 2

The final step is to add a jest.config.js file to the root of our project. The contents should simply look like:

Image 3

This is mostly boilerplate and in fact Salesforce has a simpler option which is documented in the Salesforce Developer Guide: sforce.co/2RPadyp

With this setup you can run your tests on the command line by typing:

Image 4

As you can see we don’t have any tests yet, so let’s go add one.

BUT WAIT! There’s a Simpler Way

We just saw a lot of configuration – jest.config.js files and multiple npm installs. I showed the above to reinforce that this is just standard Open Source Jest tooling – nothing Salesforce proprietary! However that doesn’t mean that the guys at Salesforce don’t want to make things easier, and they have! You can actually replace everything we just installed with one simple npm command:

Image 5

This will install everything we installed last time but behind a lwc-jest wrapper command. You can safely delete references to @lwc/jest-preset @lwc/module-resolver @lwc/compiler @lwc/engine in your package.json.

Next you can delete your jest.config.js and update your package.json scripts to be:

Image 6

Now when you run npm run test:unit it will run the lwc-jest cli command which in turn will call jest with everything installed and configured for you!

Adding a Test to an LWC Component

Each component can contain it’s own suite of tests, navigate to your component, in my project I’ll be working with a very simple helloComponent whose content is simply:

Image 7

Image 8

Add a folder called __tests__. Now create a file called helloComponent.test.js:

Image 9

Inside this file we’ll add our first test:

Image 10

There’s a lot here so let me try describe it in chunks:

Image 11

These first 2 lines import the createElement function from the lwc framework. This is the magic which knows how to take an LWC class and turn it into an HTML Element. We’ll use this further down. The second line is how we import our component that we want to test. The c/ prefix is simply the namespace of it – in Salesforce orgs the default namespace is usually c.

Image 12

Next we add our first describe, this is essentially the wrapper around where our tests live. We also add an afterEach hook. The code here will be run after every test and as stated in the comment it basically resets the DOM. In other words, it will remove any LWC elements we rendered after each test so that the next test has a clean slate.

Image 13

Finally our test! We use the createElement from the lwc import earlier to turn our HelloComponent into a real DOM element. Then we insert it into the body of our page. And finally we run our assertion – in this case I expect that there should be a tag whose text content is Hello, World. If you re-run npm run test:unit you’ll get a nice happy test!

Image 14

Testing a Property Change

Whilst a passing test is nice, it’s not really covering all our logic. One thing we should do is verify that if we set the person property to a different value, then that value is reflected in the DOM too. Let’s add a new test for that. After your existing test add the following:

Image 15

This test is pretty much the same as before except we’re now changing the value of person on the element to Mattand we’re updating our expectation to verify it now says Hello, Matt! instead. Let’s run it and see what happens:

Image 16

Hurray – It works!

Testing a Property Change Part 2

Now I want to show something that will likely catch a lot of people out. Let’s take the same test as we just did, but we’re going to move one line:

Image 17

I’ve shifted the element.person = "Matt" line to be run after the element has been inserted into the DOM. If you run the tests now you should see:

Image 18

Oh man! It fails!!! How can moving one line break a test?

The difference between the two versions of this test are that in the first version of the test we set the value of person before we inserted the element into the DOM, therefore when the element is inserted it has the value of Matt and renders it immediately. In the second version we set the value of person after the element has been inserted. This means that the component needs to be re-rendered in order to update the DOM… and rendering is asynchronous. So when we do our expectation immediately after setting the new value, the DOM hasn’t been re-rendered and so we still see Hello, World!.

So how do we fix this up? We need to run our expectations after then DOM has been updated. Fortunately there’s a nice easy way to do it… use a Promise. Let’s see what this updated test looks like:

Image 20

The change above basically creates an immediately resolving Promise and then runs our assertions on the next tick of the browser. If you run the tests now you should see 2 passing tests:

Image 20

All the code from this post is available as a github project for you to clone and run: bit.ly/2tcSHKq

Next Steps

There’s a lot more things we can do with our tests which I’ll cover in future posts including:

  • Triggering, capturing and asserting events
  • Mocking of Salesforce’s Lightning components
  • Mocking of data sources

I’d highly recommend reading some of Salesforce’s own example tests like in the e-bikes app. Here’s some additional links for more information:

Matt Goldspink

UI Architect

Matt is a Lead Engineer in Vlocity’s UI Engineering team, focused on developing beautiful user interfaces using Salesforce’s Lightning technology. Prior to Vlocity, Matt was a software trainer, teaching software development at multiple Fortune 500 companies. He also worked at Causata (now part of NiCE) and Morgan Stanley, where he led the development of their mobile platform.