testing

How to write effective tests and run them successfully.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "testing" with this command: npx skills add rsmdt/the-startup/rsmdt-the-startup-testing

Testing

How to write effective tests and run them successfully.

When to Use

  • Writing unit, integration, or E2E tests

  • Debugging test failures

  • Reviewing test quality

  • Deciding what to mock vs use real implementations

Layer Distribution

  • Unit (60-70%): Mock at boundaries only

  • Integration (20-30%): Real deps, mock external services only

  • E2E (5-10%): No mocking - real user journeys

Writing Tests by Layer

Unit Tests

Purpose: Verify isolated business logic.

Mocking rules:

  • Mock at the edge only (databases, APIs, file system, time)

  • Test the real system under test with actual implementations

  • Use real internal collaborators - mock only external boundaries

// CORRECT: Mock only external dependency const service = new OrderService(mockRepository) // Repository is the edge const total = service.calculateTotal(order) expect(total).toBe(90)

// WRONG: Mocking internal methods vi.spyOn(service, 'applyDiscount') // Now you're testing the mock

Characteristics: < 100ms, no I/O, deterministic

Test here: Business logic, validation, transformations, edge cases

Integration Tests

Purpose: Verify components work together with real dependencies.

Mocking rules:

  • Use real databases

  • Use real caches

  • Mock only external third-party services (Stripe, SendGrid)

// CORRECT: Real DB, mock external payment API const db = await createTestDatabase() const paymentApi = vi.mocked(PaymentGateway) const service = new CheckoutService(db, paymentApi)

await service.checkout(cart)

expect(await db.orders.find(orderId)).toBeDefined() // Real DB expect(paymentApi.charge).toHaveBeenCalledOnce() // Mocked external

Characteristics: < 5 seconds, containerized deps, clean state between tests

Test here: Database queries, API contracts, service communication, caching

E2E Tests

Purpose: Validate critical user journeys in the real system.

Mocking rules:

  • No mocking - that's the entire point

  • Use real services (sandbox/test modes)

  • Real browser automation

// Real browser, real system (Playwright example) await page.goto('/checkout') await page.fill('#card', '4242424242424242') await page.click('[data-testid="pay"]')

await expect(page.locator('.confirmation')).toContainText('Order confirmed')

Characteristics: < 30 seconds, critical paths only, fix flakiness immediately

Test here: Signup, checkout, auth flows, smoke tests

Core Principles

Test Behavior, Not Implementation

// CORRECT: Observable behavior expect(order.total).toBe(108)

// WRONG: Implementation detail expect(order._calculateTax).toHaveBeenCalled()

Arrange-Act-Assert

// Arrange const mockEmail = vi.mocked(EmailService) const service = new UserService(mockEmail)

// Act await service.register(userData)

// Assert expect(mockEmail.sendTo).toHaveBeenCalledWith('user@example.com')

One Behavior Per Test

Multiple assertions OK if verifying same logical outcome.

Descriptive Names

// GOOD it('rejects order when inventory insufficient', ...)

// BAD it('test order', ...)

Test Isolation

No shared mutable state between tests.

Running Tests

Execution Order

  • Lint/typecheck - Fastest feedback

  • Unit tests - Fast, high volume

  • Integration tests - Real dependencies

  • E2E tests - Highest confidence

Debugging Failures

Unit test fails:

  • Read the assertion message carefully

  • Check test setup (Arrange section)

  • Run in isolation to rule out state leakage

  • Add logging to trace execution path

Integration test fails:

  • Check database state before/after

  • Verify mocks configured correctly

  • Look for race conditions or timing issues

  • Check transaction/rollback behavior

E2E test fails:

  • Check screenshots/videos (most frameworks capture these)

  • Verify selectors still match the UI

  • Add explicit waits for async operations

  • Run locally with visible browser to observe

  • Compare CI environment to local

Flaky Tests

Handle aggressively - they erode trust:

  • Quarantine - Move to separate suite immediately

  • Fix within 1 week - Or delete

  • Common causes:

  • Shared state between tests

  • Time-dependent logic

  • Race conditions

  • Non-deterministic ordering

Coverage

Quality over quantity - 80% meaningful coverage beats 100% trivial coverage.

Focus testing effort on business-critical paths (payments, auth, core domain logic). Skip generated code.

Edge Cases

Always test:

Boundaries: min-1, min, min+1, max-1, max, max+1, zero, one, many

Special values: null, empty, negative, MAX_INT, NaN, unicode, leap years, timezones

Errors: Network failures, timeouts, invalid input, unauthorized

Anti-Patterns

Pattern Problem

Over-mocking Testing mocks instead of code

Implementation testing Breaks on refactoring

Shared state Test order affects results

Test duplication Use parameterized tests instead

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

specify-requirements

No summary provided by upstream source.

Repository SourceNeeds Review
General

review

No summary provided by upstream source.

Repository SourceNeeds Review
General

refactor

No summary provided by upstream source.

Repository SourceNeeds Review