[readonly] markdown buffer
Testing LWC Behaviour, Not Implementation Details
An LWC Jest test can be green and still tell me almost nothing useful.
expect(element.shadowRoot.querySelectorAll('div')).toHaveLength(7);
Seven div elements are probably not a requirement. A harmless markup refactor can break this test while the component remains correct.
I want tests to describe what the component promises.
Start at the public boundary
A c-case-summary might promise to accept a record ID, show loading and error states, render a subject, and dispatch caseopen when selected.
Those are behaviours. Private getters and wrapper counts are not.
Arrange the component as a consumer would:
function createComponent(properties = {}) {
const element = createElement('c-case-summary', {
is: CaseSummary
});
Object.assign(element, properties);
document.body.appendChild(element);
return element;
}
If a test must mutate private state to create a scenario, the component's input boundary may need work.
Salesforce's LWC testing guide recommends testing public APIs, interaction, DOM output, and events. The DOM still matters; assert meaningful outcomes rather than private structure.
Drive the component like a user
LWC rendering is asynchronous. After emitting wire data or resolving a promise, wait before asserting the result.
getCase.emit(mockCase);
await Promise.resolve();
expect(
element.shadowRoot.querySelector('[data-testid="subject"]').textContent
).toBe('Printer is on fire');
Cover the states the user experiences: loading, data, empty data, and error. Salesforce's wire-service Jest guide provides adapters for controlled data and failures.
Custom events are public output. Trigger the interaction and observe the event instead of calling a controller method directly.
const handler = jest.fn();
element.addEventListener('caseopen', handler);
element.shadowRoot
.querySelector('lightning-button')
.dispatchEvent(new CustomEvent('click'));
expect(handler).toHaveBeenCalledTimes(1);
expect(handler.mock.calls[0][0].detail).toEqual({
recordId: CASE_ID
});
Users do not call handleOpen(). They click a button and receive an outcome.
Keep assertions focused
Large snapshots create apparent coverage but weak review. A component changes, the snapshot produces a wall of markup, and the developer accepts the new version without checking the behaviour.
Prefer assertions that explain failures: the save button is disabled, the server error is visible, or the selected record ID was emitted.
Do not retest the internals of lightning-input or lightning-datatable. Test your contract with the base component: label, value, disabled state, and event handling.
If substantial code is pure calculation, extract it into a JavaScript module and test it directly. Let the component test prove that public inputs and interactions connect to that logic.
Know where Jest stops
Jest does not prove org permissions, metadata, Lightning page composition, real base-component rendering, or the whole user journey. Keep a smaller number of integration and end-to-end checks for those boundaries.
The best test names read like requirements: disables save while the request is running, shows field errors, or emits the selected account id.
If a name says calls handleSave, the test is probably staring inward.
Test what the component promises. Then let the component change how it keeps that promise.