Read the screenplay: FANNIEGATE — $7 trillion. 17 years. The biggest fraud in American capital markets.
⚛️ LWCAdvanced2026-03-04

Testing LWC with Jest: The Patterns That Actually Work in CI/CD

Jest testing for LWC is powerful but the setup and patterns are different from standard JavaScript testing. The key insight is that LWC Jest tests run in a simulated DOM (jsdom), not a real browser. Wire adapters are mocked. Apex calls are mocked. Navigation is mocked. You are testing your component logic and DOM manipulation, not Salesforce connectivity.

Start with @salesforce/sfdx-lwc-jest which gives you the test infrastructure. Mock your wire adapters using the emit pattern: import the adapter, call emit() with test data, then assert the DOM rendered correctly. For imperative Apex, mock the module and resolve/reject the promise. The critical pattern most developers miss is flushing the microtask queue — after emitting data or triggering events, you must await a Promise.resolve() (or use the flushPromises helper) before asserting DOM changes.

Structure your tests around user interactions. Instead of testing internal methods, simulate what a user does: click a button, enter text, select an option. Then assert what the user would see: rendered text, CSS classes, visible elements. This makes tests resilient to refactoring. I run LWC Jest tests in GitHub Actions on every PR and they catch regressions that would otherwise reach UAT.

Code Example

// __tests__/contactList.test.js
import { createElement } from 'lwc';
import ContactList from 'c/contactList';
import getContacts from '@salesforce/apex/ContactController.getContacts';

// Mock the Apex wire adapter
jest.mock(
  '@salesforce/apex/ContactController.getContacts',
  () => ({ default: jest.fn() }),
  { virtual: true }
);

// Helper: flush microtask queue
function flushPromises() {
  return new Promise(resolve => setTimeout(resolve, 0));
}

describe('c-contact-list', () => {
  afterEach(() => {
    while (document.body.firstChild) {
      document.body.removeChild(document.body.firstChild);
    }
    jest.clearAllMocks();
  });

  it('renders contacts when wire returns data', async () => {
    const element = createElement('c-contact-list', { is: ContactList });
    document.body.appendChild(element);

    // Emit mock data through the wire adapter
    getContacts.emit([
      { Id: '001', Name: 'John Doe', Email: 'john@test.com' },
      { Id: '002', Name: 'Jane Smith', Email: 'jane@test.com' }
    ]);

    // CRITICAL: flush the microtask queue
    await flushPromises();

    const rows = element.shadowRoot.querySelectorAll('tr');
    expect(rows.length).toBe(2);
    expect(rows[0].textContent).toContain('John Doe');
  });

  it('shows error when wire fails', async () => {
    const element = createElement('c-contact-list', { is: ContactList });
    document.body.appendChild(element);

    getContacts.error({ message: 'Server error' });
    await flushPromises();

    const errorEl = element.shadowRoot.querySelector('.error');
    expect(errorEl).not.toBeNull();
  });
});

Need this implemented in your org?

I've shipped these patterns in production for 10+ years.

View Consulting →

Enjoyed this? Get more like it.

Glen's Musings — AI, investing, and building things. Occasional. Free.

More LWC Tips