aaa-pattern

aaa-pattern

Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.

4estrelas
0forks
Atualizado 1/23/2026
SKILL.md
readonlyread-only
name
aaa-pattern
description

Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.

AAA Pattern (Arrange-Act-Assert)

Overview

Every test has three phases: Arrange, Act, Assert. Keep them separate.

Clear structure makes tests readable, maintainable, and debuggable. When phases blur together, tests become confusing.

When to Use

  • Writing any test
  • Test logic is hard to follow
  • Unclear what's being tested
  • Multiple actions or assertions mixed together

The Iron Rule

EVERY test must have clearly separated Arrange, Act, and Assert phases.

No exceptions:

  • Not for "it's a simple test"
  • Not for "it's more concise this way"
  • Not for "the phases are obvious"

Detection: Mixed Phases Smell

If arrange/act/assert blend together, STOP:

// ❌ VIOLATION: Phases mixed together
it('adds items to cart', () => {
  const cart = new Cart();
  cart.add({ id: '1', price: 10 });
  expect(cart.items.length).toBe(1);  // Assert in the middle
  cart.add({ id: '2', price: 20 });   // More acting
  expect(cart.total).toBe(30);        // Another assert
  expect(cart.items.length).toBe(2);  // And another
});

Problems:

  • What exactly is being tested?
  • Which action caused which result?
  • Hard to name the test accurately

The Correct Pattern: Clear Separation

// ✅ CORRECT: Clear AAA structure
it('calculates total price of all items in cart', () => {
  // Arrange
  const cart = new Cart();
  cart.add({ id: '1', name: 'Apple', price: 10 });
  cart.add({ id: '2', name: 'Banana', price: 20 });
  
  // Act
  const total = cart.getTotal();
  
  // Assert
  expect(total).toBe(30);
});

it('tracks number of items in cart', () => {
  // Arrange
  const cart = new Cart();
  
  // Act
  cart.add({ id: '1', name: 'Apple', price: 10 });
  cart.add({ id: '2', name: 'Banana', price: 20 });
  
  // Assert
  expect(cart.itemCount).toBe(2);
});

The Three Phases

Arrange

Set up the test scenario:

  • Create objects
  • Configure mocks
  • Prepare input data
  • Set initial state

Act

Execute the behavior being tested:

  • Single action (usually)
  • Call the method
  • Trigger the event
  • Make the request

Assert

Verify the outcome:

  • Check return values
  • Verify state changes
  • Confirm mock interactions
  • Validate side effects

Advanced: Given-When-Then

For BDD-style tests, same concept:

describe('Cart', () => {
  describe('when adding items', () => {
    it('should update the total', () => {
      // Given (Arrange)
      const cart = new Cart();
      const item = { id: '1', price: 25 };
      
      // When (Act)
      cart.add(item);
      
      // Then (Assert)
      expect(cart.total).toBe(25);
    });
  });
});

One Assert Per Test?

Guideline, not rule. Multiple asserts are fine if they verify ONE behavior:

// ✅ OK: Multiple asserts for one logical behavior
it('creates user with correct properties', () => {
  // Arrange
  const input = { email: 'a@b.com', name: 'Alice' };
  
  // Act
  const user = createUser(input);
  
  // Assert - all verify the creation behavior
  expect(user.id).toBeDefined();
  expect(user.email).toBe('a@b.com');
  expect(user.name).toBe('Alice');
  expect(user.createdAt).toBeInstanceOf(Date);
});
// ❌ BAD: Multiple behaviors in one test
it('user operations', () => {
  const user = createUser({ name: 'Alice' });
  expect(user.name).toBe('Alice');
  
  updateUser(user.id, { name: 'Bob' });
  expect(user.name).toBe('Bob');  // Different behavior!
  
  deleteUser(user.id);
  expect(getUser(user.id)).toBeNull();  // Yet another behavior!
});

Pressure Resistance Protocol

1. "It's More Concise"

Pressure: "Combining phases makes the test shorter"

Response: Short but confusing is worse than longer but clear.

Action: Separate the phases. Add comments if needed.

2. "It's a Simple Test"

Pressure: "For trivial tests, AAA is overkill"

Response: Consistency matters. All tests should follow the same pattern.

Action: Use AAA even for simple tests. It costs nothing.

3. "I Need Multiple Actions"

Pressure: "The behavior requires multiple steps"

Response: Multiple setup steps go in Arrange. Only the behavior being tested goes in Act.

Action: If you need multiple Acts, you probably need multiple tests.

Red Flags - STOP and Reconsider

  • expect() calls between actions
  • No clear single "act"
  • Assertions scattered throughout
  • Hard to describe what test verifies
  • Test name doesn't match structure

All of these mean: Restructure with clear AAA.

Quick Reference

Phase Contains Example
Arrange Setup, mocks, data const cart = new Cart()
Act Single behavior const total = cart.checkout()
Assert Verifications expect(total).toBe(100)

Common Rationalizations (All Invalid)

Excuse Reality
"It's more concise" Clarity beats brevity.
"The phases are obvious" Make them explicit anyway.
"Simple test, no need" Consistency matters.
"Multiple actions needed" Split into multiple tests.
"Comments are enough" Structure is better than comments.

The Bottom Line

Arrange. Act. Assert. In that order. Clearly separated.

Every test sets up (Arrange), does one thing (Act), and verifies (Assert). When phases are clear, tests are readable, debuggable, and maintainable.

You Might Also Like

Related Skills

fix

fix

243Kdev-testing

Use when you have lint errors, formatting issues, or before committing code to ensure it passes CI.

facebook avatarfacebook
Obter
peekaboo

peekaboo

179Kdev-testing

Capture and automate macOS UI with the Peekaboo CLI.

openclaw avataropenclaw
Obter
frontend-testing

frontend-testing

128Kdev-testing

Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities. Triggers on testing, spec files, coverage, Vitest, RTL, unit tests, integration tests, or write/review test requests.

langgenius avatarlanggenius
Obter
frontend-code-review

frontend-code-review

127Kdev-testing

Trigger when the user requests a review of frontend files (e.g., `.tsx`, `.ts`, `.js`). Support both pending-change reviews and focused file reviews while applying the checklist rules.

langgenius avatarlanggenius
Obter
code-reviewer

code-reviewer

92Kdev-testing

Use this skill to review code. It supports both local changes (staged or working tree) and remote Pull Requests (by ID or URL). It focuses on correctness, maintainability, and adherence to project standards.

google-gemini avatargoogle-gemini
Obter
session-logs

session-logs

90Kdev-testing

Search and analyze your own session logs (older/parent conversations) using jq.

moltbot avatarmoltbot
Obter