home / skills / yonatangross / orchestkit / unit-testing

unit-testing skill

/plugins/ork/skills/unit-testing

This skill helps you design and enforce isolated unit tests using AAA patterns, test isolation, and clear coverage goals for business logic.

npx playbooks add skill yonatangross/orchestkit --skill unit-testing

Review the files below or copy the command above to add this skill to your agents.

Files (7)
SKILL.md
5.4 KB
---
name: unit-testing
description: Unit testing patterns and best practices. Use when writing isolated unit tests, implementing AAA pattern, designing test isolation, or setting coverage targets for business logic.
tags: [testing, unit, tdd, coverage]
context: fork
agent: test-generator
version: 1.0.0
author: OrchestKit
user-invocable: false
---

# Unit Testing

Test isolated business logic with fast, deterministic tests.

## AAA Pattern (Arrange-Act-Assert)

```typescript
describe('calculateDiscount', () => {
  test('applies 10% discount for orders over $100', () => {
    // Arrange
    const order = { items: [{ price: 150 }] };

    // Act
    const result = calculateDiscount(order);

    // Assert
    expect(result).toBe(15);
  });
});
```

## Test Isolation

```typescript
describe('UserService', () => {
  let service: UserService;
  let mockRepo: MockRepository;

  beforeEach(() => {
    // Fresh instances per test
    mockRepo = createMockRepository();
    service = new UserService(mockRepo);
  });

  afterEach(() => {
    // Clean up
    vi.clearAllMocks();
  });
});
```

## Coverage Targets

| Area | Target |
|------|--------|
| Business logic | 90%+ |
| Critical paths | 100% |
| New features | 100% |
| Utilities | 80%+ |

## Parameterized Tests

```typescript
describe('isValidEmail', () => {
  test.each([
    ['[email protected]', true],
    ['invalid', false],
    ['@missing.com', false],
    ['[email protected]', true],
  ])('isValidEmail(%s) returns %s', (email, expected) => {
    expect(isValidEmail(email)).toBe(expected);
  });
});
```

## Python Example

```python
import pytest

class TestCalculateDiscount:
    def test_applies_discount_over_threshold(self):
        # Arrange
        order = Order(total=150)

        # Act
        discount = calculate_discount(order)

        # Assert
        assert discount == 15

    @pytest.mark.parametrize("total,expected", [
        (100, 0),
        (101, 10.1),
        (200, 20),
    ])
    def test_discount_thresholds(self, total, expected):
        order = Order(total=total)
        assert calculate_discount(order) == expected
```

## Fixture Scoping (2026 Best Practice)

```python
import pytest

# Function scope (default): Fresh instance per test - ISOLATED
@pytest.fixture(scope="function")
def db_session():
    """Each test gets clean database state."""
    session = create_session()
    yield session
    session.rollback()  # Cleanup

# Module scope: Shared across all tests in file - EFFICIENT
@pytest.fixture(scope="module")
def expensive_model():
    """Load once per test file (expensive setup)."""
    return load_large_ml_model()  # 5 seconds to load

# Session scope: Shared across ALL tests - MOST EFFICIENT
@pytest.fixture(scope="session")
def db_engine():
    """Single connection pool for entire test run."""
    engine = create_engine(TEST_DB_URL)
    Base.metadata.create_all(engine)
    yield engine
    Base.metadata.drop_all(engine)
```

**When to use each scope:**
| Scope | Use Case | Example |
|-------|----------|---------|
| function | Isolated tests, mutable state | db_session, mock objects |
| module | Expensive setup, read-only | ML model, compiled regex |
| session | Very expensive, immutable | DB engine, external service |

## Indirect Parametrization

```python
# Defer expensive setup from collection to runtime
@pytest.fixture
def user(request):
    """Create user with different roles based on parameter."""
    role = request.param  # Receives value from parametrize
    return UserFactory(role=role)

@pytest.mark.parametrize("user", ["admin", "moderator", "viewer"], indirect=True)
def test_permissions(user):
    """Test runs 3 times with different user roles."""
    # user fixture is called with each role
    assert user.can_access("/dashboard") == (user.role in ["admin", "moderator"])

# Combinatorial testing with stacked decorators
@pytest.mark.parametrize("role", ["admin", "user"])
@pytest.mark.parametrize("status", ["active", "suspended"])
def test_access_matrix(role, status):
    """Runs 4 tests: admin/active, admin/suspended, user/active, user/suspended"""
    user = User(role=role, status=status)
    expected = (role == "admin" and status == "active")
    assert user.can_modify() == expected
```

## Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Framework | Vitest (modern), Jest (mature), pytest |
| Execution | < 100ms per test |
| Dependencies | None (mock everything external) |
| Coverage tool | c8, nyc, pytest-cov |

## Common Mistakes

- Testing implementation, not behavior
- Slow tests (external calls)
- Shared state between tests
- Over-mocking (testing mocks not code)

## Related Skills

- `integration-testing` - Testing interactions
- `msw-mocking` - Network mocking
- `test-data-management` - Fixtures and factories

## Capability Details

### pytest-patterns
**Keywords:** pytest, python, fixture, parametrize
**Solves:**
- Write pytest unit tests
- Use fixtures effectively
- Parametrize test cases

### vitest-patterns
**Keywords:** vitest, jest, typescript, mock
**Solves:**
- Write Vitest unit tests
- Mock dependencies
- Test React components

### orchestkit-strategy
**Keywords:** orchestkit, strategy, coverage, pyramid
**Solves:**
- OrchestKit test strategy example
- Test coverage targets
- Testing pyramid ratios

### test-case-template
**Keywords:** template, test, structure, arrange
**Solves:**
- Test case template
- Arrange-Act-Assert structure
- Copy-paste test starter

Overview

This skill provides practical unit-testing patterns and best practices for fast, deterministic tests focused on isolated business logic. It covers Arrange-Act-Assert structure, test isolation, parameterized tests, fixture scoping, coverage targets, and common pitfalls for TypeScript and Python projects.

How this skill works

The skill teaches test structure and patterns that keep unit tests small, fast, and independent. It shows how to create fresh instances per test, mock external dependencies, use parameterized tests for coverage, and choose fixture scopes that balance isolation and performance. It also gives concrete coverage targets and recommended tools.

When to use it

  • Writing isolated unit tests for business logic functions or services
  • Implementing Arrange-Act-Assert style test cases
  • Designing test isolation and fixture lifecycles to avoid shared state
  • Setting coverage targets for new features and critical paths
  • Converting slow integration checks into fast unit tests with mocks

Best practices

  • Follow AAA: Arrange test data, Act on the unit under test, Assert observable behavior
  • Create fresh instances in beforeEach/fixture-scope function to avoid shared state
  • Mock all external dependencies; tests should have no network or DB calls
  • Prefer parameterized tests to cover multiple input cases without duplication
  • Aim for strict coverage on business logic (90%+), 100% on critical paths and new features

Example use cases

  • Test a discount calculation using Arrange-Act-Assert with multiple totals via parameterized tests
  • Verify UserService behavior by injecting a fresh mock repository per test
  • Use function-scoped fixtures for mutable state and module/session scopes for expensive immutable setup
  • Define coverage policy: business logic 90%+, utilities 80%+, new feature files 100%
  • Convert flaky integration checks to unit tests that mock external APIs with MSW or equivalent

FAQ

How do I choose fixture scope?

Use function scope for complete isolation and mutable state, module scope for expensive read-only setup, and session scope for very expensive immutable resources like a DB engine.

What should I mock in unit tests?

Mock all external systems (HTTP, DB, queues, file systems). Keep tests focused on the unit's behavior, not external integrations.