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:

  1. Jest setup and file organization
  2. Core test structure and interaction testing
  3. Wire and Apex mocking patterns
  4. Accessibility and snapshot testing
  5. Final best-practices checklist

Setup:

  • Install dependencies: npm install --save-dev @salesforce/sfdx-lwc-jest
  • Configure Jest in jest.config.js
  • Run tests: npm test or sf 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