LWC Testing Standards
For org-wide testing policies, coverage requirements, and the 1:1 test method to code method relationship, see Testing Policy.
Note: LWC testing is currently a work in progress and not required as of right now. The standards outlined below are potential standards and serve as discussion points for future implementation. These guidelines may evolve as LWC testing practices mature and become a requirement.
Jest Testing Framework
Usage: Use Jest with @salesforce/sfdx-lwc-jest for LWC testing.
Recommended reading order for this page:
- Jest setup and file organization
- Core test structure and interaction testing
- Wire and Apex mocking patterns
- Accessibility and snapshot testing
- Final best-practices checklist
Setup:
- Install dependencies:
npm install --save-dev @salesforce/sfdx-lwc-jest - Configure Jest in
jest.config.js - Run tests:
npm testorsf lwc test
Guidelines:
- Write tests for all public methods and event handlers
- Follow 1:1 test method to code method relationship when possible
- Test user interactions and events
- Mock wire adapters and Apex methods
- Test error scenarios
Test File Organization
- Test files should be co-located with components
- Test file naming:
componentName.test.js - Test files should mirror component structure
Example Structure:
src/
└── components/
└── lwc/
└── exampleChild/
├── exampleChild.js
├── exampleChild.html
├── exampleChild.css
├── exampleChild.js-meta.xml
└── __tests__/
└── exampleChild.test.js
Component Testing Patterns
Basic Test Structure
import { createElement } from 'lwc';
import ExampleChild from 'c/exampleChild';
describe('c-example-child', () => {
afterEach(() => {
// Cleanup DOM after each test
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
});
it('renders component successfully', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
document.body.appendChild(element);
return Promise.resolve().then(() => {
// Assertions
expect(element).not.toBeNull();
});
});
});
Component API and Event Patterns
Example Test Structure:
import { createElement } from 'lwc';
import ExampleChild from 'c/exampleChild';
import { NavigationMixin } from 'lightning/navigation';
describe('c-example-child', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
});
it('renders button with label', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
element.label = 'Click Me';
document.body.appendChild(element);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
expect(button).not.toBeNull();
expect(button.textContent).toBe('Click Me');
});
});
it('dispatches actionclicked event on click', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
document.body.appendChild(element);
const handler = jest.fn();
element.addEventListener('actionclicked', handler);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
button.click();
expect(handler).toHaveBeenCalled();
});
});
it('calls focus method successfully', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
document.body.appendChild(element);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
const focusSpy = jest.spyOn(button, 'focus');
element.focus();
expect(focusSpy).toHaveBeenCalled();
});
});
});
Mocking Wire Adapters
Usage: Mock wire adapters to control test data.
Example:
import { createElement } from 'lwc';
import AccountDetail from 'c/accountDetail';
import { getRecord } from 'lightning/uiRecordApi';
// Mock the wire adapter
jest.mock(
'lightning/uiRecordApi',
() => ({
getRecord: jest.fn()
}),
{ virtual: true }
);
describe('c-account-detail', () => {
it('displays account name from wire adapter', () => {
const mockRecord = {
fields: {
Name: { value: 'Test Account' }
}
};
getRecord.mockResolvedValue(mockRecord);
const element = createElement('c-account-detail', {
is: AccountDetail
});
element.recordId = '001000000000000AAA';
document.body.appendChild(element);
return Promise.resolve().then(() => {
const nameElement = element.shadowRoot.querySelector('.account-name');
expect(nameElement.textContent).toBe('Test Account');
});
});
});
Mocking Apex Methods
Usage: Mock Apex method calls using jest.fn().
Example:
import { createElement } from 'lwc';
import AccountList from 'c/accountList';
import getAccounts from '@salesforce/apex/ACME_AccountService.getAccounts';
// Mock Apex method
jest.mock(
'@salesforce/apex/ACME_AccountService.getAccounts',
() => ({ default: jest.fn() }),
{ virtual: true }
);
describe('c-account-list', () => {
it('displays accounts from Apex method', () => {
const mockAccounts = [
{ Id: '001001', Name: 'Account 1' },
{ Id: '001002', Name: 'Account 2' }
];
getAccounts.mockResolvedValue(mockAccounts);
const element = createElement('c-account-list', {
is: AccountList
});
document.body.appendChild(element);
return Promise.resolve().then(() => {
const accountElements = element.shadowRoot.querySelectorAll('.account-item');
expect(accountElements.length).toBe(2);
});
});
});
LWC Mocking Strategies
Usage: Mock wire adapters, Apex methods, and modules in LWC tests.
Example:
// Mock wire adapter
jest.mock(
'lightning/uiRecordApi',
() => ({
getRecord: jest.fn()
}),
{ virtual: true }
);
// Mock Apex method
jest.mock(
'@salesforce/apex/ACME_AccountService.getAccounts',
() => ({ default: jest.fn() }),
{ virtual: true }
);
// Mock module
jest.mock('c/utils', () => ({
formatDate: jest.fn((date) => date.toISOString())
}));
When to Mock vs. Use Real Data (LWC)
Mock when:
- Testing error scenarios
- Testing with specific data that's hard to create
- Testing external integrations
- Improving test performance
Use real data when:
- Testing data transformation logic
- Testing component behavior with actual data
- Testing complex business logic
- Integration testing
Mock Data Patterns (LWC)
Create reusable mock data generators.
Example:
// Mock data generator
const createMockAccount = (overrides = {}) => {
return {
Id: '001000000000000AAA',
Name: 'Test Account',
Type: 'Customer',
...overrides
};
};
// Usage in tests
it('displays account name', () => {
const mockAccount = createMockAccount({ Name: 'Custom Account' });
getRecord.mockResolvedValue({ fields: { Name: { value: mockAccount.Name } } });
// Test implementation
});
Event Testing
Usage: Test component events (custom events and standard events).
Example:
import { createElement } from 'lwc';
import ExampleChild from 'c/exampleChild';
describe('c-example-child', () => {
it('dispatches actionclicked event on button click', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
document.body.appendChild(element);
const handler = jest.fn();
element.addEventListener('actionclicked', handler);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
button.click();
expect(handler).toHaveBeenCalled();
const event = handler.mock.calls[0][0];
expect(event.detail).toBeDefined();
});
});
});
User Interaction Testing
Usage: Test user interactions (clicks, input, keyboard events).
Example:
import { createElement } from 'lwc';
import AccountForm from 'c/accountForm';
describe('c-account-form', () => {
it('updates account name on input change', () => {
const element = createElement('c-account-form', {
is: AccountForm
});
document.body.appendChild(element);
return Promise.resolve().then(() => {
const input = element.shadowRoot.querySelector('lightning-input');
input.value = 'New Account Name';
input.dispatchEvent(new CustomEvent('change'));
return Promise.resolve().then(() => {
expect(element.accountName).toBe('New Account Name');
});
});
});
it('submits form on button click', () => {
const element = createElement('c-account-form', {
is: AccountForm
});
document.body.appendChild(element);
const submitHandler = jest.fn();
element.addEventListener('formsubmit', submitHandler);
return Promise.resolve().then(() => {
const submitButton = element.shadowRoot.querySelector('lightning-button');
submitButton.click();
expect(submitHandler).toHaveBeenCalled();
});
});
});
Accessibility Testing in Tests
Usage: Test accessibility features (ARIA labels, keyboard navigation).
Example:
import { createElement } from 'lwc';
import ExampleChild from 'c/exampleChild';
describe('c-example-child', () => {
it('has proper ARIA label', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
element.label = 'Submit Form';
element.ariaLabel = 'Submit form';
document.body.appendChild(element);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
expect(button.getAttribute('aria-label')).toBe('Submit form');
});
});
it('is keyboard accessible', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
document.body.appendChild(element);
return Promise.resolve().then(() => {
const button = element.shadowRoot.querySelector('button');
// Simulate keyboard interaction
const keydownEvent = new KeyboardEvent('keydown', { key: 'Enter' });
button.dispatchEvent(keydownEvent);
// Verify action was triggered
expect(button).toBeDefined();
});
});
});
Snapshot Testing
Usage: Use snapshot testing for UI regression testing (optional).
Example:
import { createElement } from 'lwc';
import ExampleChild from 'c/exampleChild';
describe('c-example-child', () => {
it('matches snapshot', () => {
const element = createElement('c-example-child', {
is: ExampleChild
});
element.label = 'Click Me';
document.body.appendChild(element);
return Promise.resolve().then(() => {
expect(element).toMatchSnapshot();
});
});
});
Testing Best Practices
- Write tests for all public methods
- Test event dispatching and handling
- Mock wire adapters and Apex methods
- Test error scenarios
- Test accessibility features
- Follow 1:1 test method to code method relationship when possible
- Use descriptive test names
- Clean up after each test
Change History
Version 1.0 - 2026-03-26
- Added initial version history tracking for this document.
Last Updated: 2026-03-26 Version: 1.0