refactor: provide mock builder for test repo stubs #15

Open
opened 2026-05-30 01:32:53 +00:00 by GKaszewski · 0 comments
Owner

Problem

Tests in tests/integration.rs and tests/activities.rs must implement all methods of every repository trait (19 for FollowRepository, 7 for ActorRepository, etc.) even when testing a single scenario. Most stubs return Ok(()) or Ok(vec![]).

Adding a new method to any trait breaks all test code. Test intent is buried under boilerplate — it's hard to see what a test actually exercises.

Proposal

Provide a configurable mock builder behind #[cfg(test)] or a test-support feature:

let follow_repo = MockFollowRepo::builder()
    .on_add_follower(|_, _, _, _| Ok(()))
    .on_get_followers(|uid| Ok(vec![follower_fixture()]))
    .build();

Unregistered methods return sensible defaults (Ok(()) for writes, Ok(None) / Ok(vec![]) for reads). Optionally track call counts for assertions.

Alternatively, use a #[derive(DefaultMock)] proc macro or a hand-written MemRepo with configurable overrides.

Files

  • New: src/testing.rs or src/test_support/mod.rs
  • src/tests/integration.rs (consumer, simplify)
  • src/tests/activities.rs (consumer, simplify)

Trade-offs

  • Dramatically reduces test boilerplate
  • Mock builder adds maintenance surface but pays for itself quickly
  • Proc macro approach is fancier but harder to debug
  • Should be done after repo trait changes (#12) settle to avoid churn

Dependency

Best done after #12 (repository trait refactor) since the trait shape determines mock ergonomics.

## Problem Tests in `tests/integration.rs` and `tests/activities.rs` must implement all methods of every repository trait (19 for FollowRepository, 7 for ActorRepository, etc.) even when testing a single scenario. Most stubs return `Ok(())` or `Ok(vec![])`. Adding a new method to any trait breaks all test code. Test intent is buried under boilerplate — it's hard to see what a test actually exercises. ## Proposal Provide a configurable mock builder behind `#[cfg(test)]` or a `test-support` feature: ```rust let follow_repo = MockFollowRepo::builder() .on_add_follower(|_, _, _, _| Ok(())) .on_get_followers(|uid| Ok(vec![follower_fixture()])) .build(); ``` Unregistered methods return sensible defaults (`Ok(())` for writes, `Ok(None)` / `Ok(vec![])` for reads). Optionally track call counts for assertions. Alternatively, use a `#[derive(DefaultMock)]` proc macro or a hand-written `MemRepo` with configurable overrides. ## Files - New: `src/testing.rs` or `src/test_support/mod.rs` - `src/tests/integration.rs` (consumer, simplify) - `src/tests/activities.rs` (consumer, simplify) ## Trade-offs - Dramatically reduces test boilerplate - Mock builder adds maintenance surface but pays for itself quickly - Proc macro approach is fancier but harder to debug - Should be done after repo trait changes (#12) settle to avoid churn ## Dependency Best done after #12 (repository trait refactor) since the trait shape determines mock ergonomics.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GKaszewski/k-ap#15