Files
freemo d9e5668cec fix(skills): comprehensive final audit pass for programming-patterns skill
Fixes and improvements from exhaustive audit:

Consistency fixes in SKILL.md:
- 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected)
- 'Singleton for factory instance' → clarified to 'register factory as
  singleton-scoped via DI container' (less misleading wording)
- Documentation Format section updated with note that SKILL.md itself is the
  authoritative source for related-pattern combinations

Coverage fix — Related Patterns sections:
- Added '## Related Patterns' to ALL 94 pattern files (was 0/94)
- Each section lists 3–6 related patterns with relationship descriptions
- Covers: why they're related, when to prefer one vs the other,
  and which are often confused

SOLID principles → Creational → Structural → Behavioral → Architectural →
Concurrency → Functional → Resilience → Data Access → Messaging →
Testing → Error Handling → Microservice — all 13 categories covered

Code verification:
- Python: 0 failures (all 85 testable blocks pass)
- Go: 0 failures (all 76 testable blocks pass)
- JavaScript: 0 failures (all 78 testable blocks pass)
- All 239 code blocks verified correct after edits

Final skill state:
- 108 files, 36,524 lines across 13 reference categories
- 94/94 pattern files have Related Patterns sections
- 2,815-line SKILL.md with 67 decision trees, 23 scenarios,
  0 broken references, 0 naming inconsistencies
2026-04-15 13:22:13 -04:00

7.6 KiB

Arrange-Act-Assert (AAA)

Problem

Tests become hard to read when setup, execution, and verification are interleaved. Reviewers can't quickly tell what is being tested, how, or what the expected outcome is. This leads to maintenance burden and missed bugs.

Solution

Structure every test in exactly three phases:

  1. Arrange — Set up the preconditions: create objects, configure stubs, prepare input data.
  2. Act — Execute the single behaviour under test.
  3. Assert — Verify the outcome: return values, state changes, or interactions.

Keep each phase visually separated (blank line or comment). The Act phase should be a single statement or very small cluster.

When to Use

  • Any unit or integration test in any language.
  • When you want a consistent, scannable test format across a team.
  • Code reviews benefit from immediately visible structure.

When to Avoid

  • Very trivial one-liner assertions where the structure adds noise.
  • BDD-style acceptance tests where Given-When-Then is a better fit.
  • Property-based tests where the framework generates arrange and assert.

Pseudocode

function test_transfer_deducts_from_source():
    // Arrange
    source = Account(balance=100)
    target = Account(balance=50)

    // Act
    transfer(source, target, amount=30)

    // Assert
    assert source.balance == 70
    assert target.balance == 80

Python

"""Arrange-Act-Assert pattern in Python."""
from dataclasses import dataclass


@dataclass
class Account:
    owner: str
    balance: float

    def deposit(self, amount: float) -> None:
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self.balance += amount

    def withdraw(self, amount: float) -> None:
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        self.balance -= amount


def transfer(source: Account, target: Account, amount: float) -> None:
    source.withdraw(amount)
    target.deposit(amount)


# ── Tests ────────────────────────────────────────────────────

def test_transfer_deducts_from_source():
    # Arrange
    source = Account(owner="Alice", balance=100.0)
    target = Account(owner="Bob", balance=50.0)

    # Act
    transfer(source, target, amount=30.0)

    # Assert
    assert source.balance == 70.0
    assert target.balance == 80.0
    print(f"PASS: source={source.balance}, target={target.balance}")


def test_transfer_insufficient_funds():
    # Arrange
    source = Account(owner="Alice", balance=10.0)
    target = Account(owner="Bob", balance=50.0)

    # Act & Assert
    try:
        transfer(source, target, amount=50.0)
        assert False, "Expected ValueError"
    except ValueError as e:
        assert str(e) == "Insufficient funds"
        print(f"PASS: got expected error: {e}")


def test_deposit_positive_amount():
    # Arrange
    account = Account(owner="Carol", balance=0.0)

    # Act
    account.deposit(25.0)

    # Assert
    assert account.balance == 25.0
    print(f"PASS: balance after deposit={account.balance}")


if __name__ == "__main__":
    test_transfer_deducts_from_source()
    test_transfer_insufficient_funds()
    test_deposit_positive_amount()
    print("\nAll AAA tests passed.")

Go

// arrange_act_assert_test.go
package main

import (
	"errors"
	"fmt"
)

// ── Production code ──────────────────────────────────────────

type Account struct {
	Owner   string
	Balance float64
}

func (a *Account) Deposit(amount float64) error {
	if amount <= 0 {
		return errors.New("deposit must be positive")
	}
	a.Balance += amount
	return nil
}

func (a *Account) Withdraw(amount float64) error {
	if amount > a.Balance {
		return errors.New("insufficient funds")
	}
	a.Balance -= amount
	return nil
}

func Transfer(source, target *Account, amount float64) error {
	if err := source.Withdraw(amount); err != nil {
		return err
	}
	return target.Deposit(amount)
}

// ── Tests ────────────────────────────────────────────────────

func testTransferDeductsFromSource() {
	// Arrange
	source := &Account{Owner: "Alice", Balance: 100.0}
	target := &Account{Owner: "Bob", Balance: 50.0}

	// Act
	err := Transfer(source, target, 30.0)

	// Assert
	if err != nil {
		panic(fmt.Sprintf("unexpected error: %v", err))
	}
	if source.Balance != 70.0 || target.Balance != 80.0 {
		panic("balance mismatch")
	}
	fmt.Printf("PASS: source=%.0f, target=%.0f\n", source.Balance, target.Balance)
}

func testTransferInsufficientFunds() {
	// Arrange
	source := &Account{Owner: "Alice", Balance: 10.0}
	target := &Account{Owner: "Bob", Balance: 50.0}

	// Act
	err := Transfer(source, target, 50.0)

	// Assert
	if err == nil {
		panic("expected error")
	}
	if err.Error() != "insufficient funds" {
		panic("wrong error message")
	}
	fmt.Printf("PASS: got expected error: %v\n", err)
}

func testDepositPositiveAmount() {
	// Arrange
	account := &Account{Owner: "Carol", Balance: 0.0}

	// Act
	err := account.Deposit(25.0)

	// Assert
	if err != nil {
		panic(fmt.Sprintf("unexpected error: %v", err))
	}
	if account.Balance != 25.0 {
		panic("balance mismatch")
	}
	fmt.Printf("PASS: balance after deposit=%.0f\n", account.Balance)
}

func main() {
	testTransferDeductsFromSource()
	testTransferInsufficientFunds()
	testDepositPositiveAmount()
	fmt.Println("\nAll AAA tests passed.")
}

JavaScript

// arrange_act_assert.js

class Account {
  constructor(owner, balance) {
    this.owner = owner;
    this.balance = balance;
  }

  deposit(amount) {
    if (amount <= 0) throw new Error("Deposit must be positive");
    this.balance += amount;
  }

  withdraw(amount) {
    if (amount > this.balance) throw new Error("Insufficient funds");
    this.balance -= amount;
  }
}

function transfer(source, target, amount) {
  source.withdraw(amount);
  target.deposit(amount);
}

// ── Tests ────────────────────────────────────────────────────

function testTransferDeductsFromSource() {
  // Arrange
  const source = new Account("Alice", 100);
  const target = new Account("Bob", 50);

  // Act
  transfer(source, target, 30);

  // Assert
  console.assert(source.balance === 70);
  console.assert(target.balance === 80);
  console.log(`PASS: source=${source.balance}, target=${target.balance}`);
}

function testTransferInsufficientFunds() {
  // Arrange
  const source = new Account("Alice", 10);
  const target = new Account("Bob", 50);

  // Act & Assert
  try {
    transfer(source, target, 50);
    throw new Error("Expected error");
  } catch (e) {
    console.assert(e.message === "Insufficient funds");
    console.log(`PASS: got expected error: ${e.message}`);
  }
}

function testDepositPositiveAmount() {
  // Arrange
  const account = new Account("Carol", 0);

  // Act
  account.deposit(25);

  // Assert
  console.assert(account.balance === 25);
  console.log(`PASS: balance after deposit=${account.balance}`);
}

testTransferDeductsFromSource();
testTransferInsufficientFunds();
testDepositPositiveAmount();
console.log("\nAll AAA tests passed.");
  • Given-When-Then — Given-When-Then is the BDD equivalent of AAA; semantically identical but phrased for stakeholder readability.
  • Specification — a specification pattern can formalise the assertions in the Assert phase.
  • Test Data Builder — use a Test Data Builder to make the Arrange phase concise and expressive.