LWC supports unit testing via Jest running locally in Node.js — no Salesforce org needed.
Setup:
bash npm install sf force lightning lwc test setup # one-time setup
Generates jest.config.js and force-app/main/default/lwc/.../mycomponent.test.js files.
Anatomy of a Jest test:
`javascript import { createElement } from 'lwc'; import MyComponent from 'c/myComponent';
describe('c-my-component', () => { afterEach(() => { // Reset DOM between tests while (document.body.firstChild) { document.body.removeChild(document.body.firstChild); } });
it('renders the title', () => { const element = createElement('c-my-component', { is: MyComponent }); element.title = 'Hello'; document.body.appendChild(element);
const h1 = element.shadowRoot.querySelector('h1'); expect(h1.textContent).toBe('Hello'); });
it('dispatches a userselected event on click', () => { const element = createElement('c-my-component', { is: MyComponent }); document.body.appendChild(element);
const handler = jest.fn(); element.addEventListener('userselected', handler);
const button = element.shadowRoot.querySelector('button'); button.click();
expect(handler).toHaveBeenCalled(); }); }); `
Mocking Apex methods:
`javascript import getAccount from '@salesforce/apex/AccountController.getAccount';
jest.mock('@salesforce/apex/AccountController.getAccount', () => ({ default: jest.fn() }), { virtual: true });
it('handles wire data', async () => { getAccount.mockResolvedValue({ Id: '001x', Name: 'Acme' }); const element = createElement('c-my-component', { is: MyComponent }); document.body.appendChild(element); await Promise.resolve(); // wait for wire to resolve
const span = element.shadowRoot.querySelector('span'); expect(span.textContent).toBe('Acme'); }); `
Mocking Lightning Data Service:
Use @salesforce/sfdx-lwc-jest mocks for getRecord, getRecordCreateDefaults, etc.
Mocking Lightning Message Service:
createElement + subscribe/publish mocks.
Patterns:
- Test the public API only —
@apiproperties and dispatched events. Don't test private state. - Async resolution — wires resolve on next microtask; use
await Promise.resolve()orawait flushPromises()between actions and assertions. - No `connectedCallback` race — the component renders synchronously after
appendChild. - Coverage — Jest reports coverage; pair with PMD on Apex for full project metrics.
- CI integration — Jest runs in PR validation pipelines, headless.
Anti-patterns:
- Testing implementation details (private state, internal method names).
- Tests that depend on real Salesforce — use mocks.
- Snapshot tests of HTML output — fragile, low value.
- Skipping accessibility checks — use
@testing-library/jest-domor axe for a11y.
LWC + Jest brings front-end testing maturity to Salesforce. Most LWC-heavy orgs aim for 70-80% LWC test coverage alongside 75% Apex coverage.
