Well-structured tests are easier to read, debug, and maintain. This lesson introduces the Arrange-Act-Assert pattern and other conventions for organizing Jest test files as your suite grows.
The Arrange-Act-Assert Pattern
Arrange-Act-Assert (AAA) is a simple, widely used structure for individual tests. "Arrange" sets up any data or mocks needed. "Act" calls the function or triggers the behavior being tested. "Assert" checks the result with expect(). Keeping these three phases visually separated makes tests easy to scan.
You don't need comments labeling each phase in every test, but keeping the underlying structure consistent — setup, then action, then assertion — makes even unfamiliar tests quick to understand.
test('applies a 10% discount to orders over $100', () => {
// Arrange
const order = { total: 150 };
// Act
const result = applyDiscount(order);
// Assert
expect(result.total).toBe(135);
});
Even without the comments, the three phases are visually distinct: build data, call the function, check the result.
One Focused Assertion Concept Per Test
describe('applyDiscount', () => {
it('applies a 10% discount over $100', () => { /* ... */ });
it('applies no discount at exactly $100', () => { /* ... */ });
it('throws for a negative total', () => { /* ... */ });
});
Each it() should test one behavior, not several unrelated behaviors bundled together.
Multiple expect() calls in one test are fine if they check the same behavior from different angles.
Splitting behaviors into separate tests makes failures pinpoint the exact broken case.
Shared setup across tests belongs in beforeEach(), not repeated inline in every test.
Test Structure Cheat Sheet
Quick guidelines for organizing any Jest test file.
Guideline
Why It Matters
Arrange-Act-Assert
Keeps each test easy to scan
One behavior per test
Failures point to a specific case
Descriptive test names
Failure reports are self-explanatory
Shared setup in beforeEach()
Avoids duplicated arrange code
Group related tests with describe()
Improves readability of output
Avoid logic/branches in tests
Keeps tests predictable and simple
Avoid Conditional Logic Inside Tests
A test with if statements or loops that change what gets asserted is a sign the test is trying to do too much. Tests should be simple and linear — if a condition is genuinely important, split it into its own dedicated test case instead of branching inside one.
// Avoid: conditional logic inside a test
test('validates user', () => {
const user = getUser();
if (user.role === 'admin') {
expect(user.permissions.length).toBeGreaterThan(0);
} else {
expect(user.permissions.length).toBe(0);
}
});
// Prefer: two focused tests
test('admin users have permissions', () => {
expect(getUser('admin').permissions.length).toBeGreaterThan(0);
});
test('regular users have no permissions', () => {
expect(getUser('member').permissions.length).toBe(0);
});
Organizing Larger Test Files
As a module grows, group its tests by method or feature using nested describe() blocks, and keep the file ordered the same way the source file is ordered. This makes it trivial to find the tests for any given function.
One top-level describe() per module or class.
One nested describe() per method or exported function.
Shared fixtures/mocks declared once near the top of the file.
Edge cases and error paths grouped together, clearly labeled.
Common Mistakes
Cramming multiple unrelated behaviors into a single test() block.
Adding if/else branches inside a test instead of splitting into separate cases.
Duplicating the same setup code in every test instead of using beforeEach().
Naming tests after implementation details instead of observable behavior.
Key Takeaways
Arrange-Act-Assert keeps individual tests easy to read at a glance.
Each test should focus on one behavior so failures are easy to diagnose.
Avoid conditional logic inside tests; split branches into separate test cases.
Nested describe() blocks that mirror the source file structure keep large suites navigable.
Pro Tip
If you find yourself writing a comment to explain what a test is really checking, that's usually a sign the test name itself should be more specific instead.
You now know how to structure clean, readable Jest tests. Next, dive into the most common matchers you'll use in everyday testing.