aaa-pattern
Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Use when designing or modifying APIs. Use when adding breaking changes. Use when clients depend on API stability.
Use when implementing authentication. Use when storing passwords. Use when asked to store credentials insecurely.
Use when same data is fetched repeatedly. Use when database queries are slow. Use when implementing caching without invalidation strategy.
Use when tempted to use class inheritance. Use when creating class hierarchies. Use when subclass needs only some parent behavior.
Use when acquiring multiple locks. Use when operations wait for each other. Use when system hangs without crashing.
Use when a class creates its own dependencies. Use when instantiating concrete implementations inside a class. Use when told to avoid dependency injection for simplicity.
| name | aaa-pattern |
| description | Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed. |
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.
EVERY test must have clearly separated Arrange, Act, and Assert phases.
No exceptions:
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:
// ✅ 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);
});
Set up the test scenario:
Execute the behavior being tested:
Verify the outcome:
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);
});
});
});
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: "Combining phases makes the test shorter"
Response: Short but confusing is worse than longer but clear.
Action: Separate the phases. Add comments if needed.
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.
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.
expect() calls between actionsAll of these mean: Restructure with clear AAA.
| Phase | Contains | Example |
|---|---|---|
| Arrange | Setup, mocks, data | const cart = new Cart() |
| Act | Single behavior | const total = cart.checkout() |
| Assert | Verifications | expect(total).toBe(100) |
| 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. |
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.