Add A-Frame architecture and Testing Without Mocks guidelines
Document James Shore's patterns for clean architecture: - A-Frame: Logic and Infrastructure as independent peers - Testing Without Mocks: state-based tests, Nullables, no mocking - Testing approach by layer (Logic, Infrastructure, Application) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
67657bf717
commit
9cbbb045b9
1 changed files with 50 additions and 0 deletions
50
AGENTS.md
50
AGENTS.md
|
|
@ -34,6 +34,56 @@ A pre-commit hook is provided that runs `mix format --check-formatted` and `mix
|
||||||
- **Prefer small, focused commits** - each commit should represent a single logical change
|
- **Prefer small, focused commits** - each commit should represent a single logical change
|
||||||
- **Write tests first** - follow test-driven development (TDD) where practical; write failing tests before implementing features
|
- **Write tests first** - follow test-driven development (TDD) where practical; write failing tests before implementing features
|
||||||
- Use `mix precommit` alias when you are done with all changes and fix any pending issues
|
- Use `mix precommit` alias when you are done with all changes and fix any pending issues
|
||||||
|
|
||||||
|
### Architecture: A-Frame Pattern
|
||||||
|
|
||||||
|
This project follows [James Shore's A-Frame Architecture](https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks) where Logic and Infrastructure are independent peers coordinated at the Application layer:
|
||||||
|
|
||||||
|
```
|
||||||
|
Application/UI Values
|
||||||
|
/ \
|
||||||
|
V V
|
||||||
|
Logic Infrastructure
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Logic** - Pure business logic with no external dependencies; easily testable with state-based tests
|
||||||
|
- **Infrastructure** - Wrappers around external systems (database, APIs, file system); one wrapper per external system
|
||||||
|
- **Application** - Coordinates Logic and Infrastructure; thin layer that wires things together
|
||||||
|
- **Values** - Immutable data structures passed between layers
|
||||||
|
|
||||||
|
### Testing: Without Mocks
|
||||||
|
|
||||||
|
Follow the "Testing Without Mocks" pattern language. Key principles:
|
||||||
|
|
||||||
|
- **Avoid mocks** - They test implementation details and break when refactoring
|
||||||
|
- **State-based tests** - Check output/state, not function calls
|
||||||
|
- **Narrow tests** - Focus on specific functions/behaviors, not system-wide functionality
|
||||||
|
- **Overlapping sociable tests** - Execute real dependencies; dependencies have their own thorough tests
|
||||||
|
|
||||||
|
#### Nullables Pattern
|
||||||
|
|
||||||
|
For infrastructure code, implement **Nullables** - production code with an off-switch for external communication:
|
||||||
|
|
||||||
|
- Create a factory method that returns a "null" version for testing (disables external calls but preserves behavior)
|
||||||
|
- Nullables have legitimate production uses (dry-run modes, cache warming)
|
||||||
|
- Test Nullables like any production code
|
||||||
|
|
||||||
|
#### Infrastructure Wrappers
|
||||||
|
|
||||||
|
- One wrapper module per external system (Repo for database, HTTP clients for APIs)
|
||||||
|
- Expose clean interfaces matching application needs, not third-party APIs
|
||||||
|
- These are the **only** places where external communication occurs
|
||||||
|
- Test with narrow integration tests against real systems (test database, etc.)
|
||||||
|
|
||||||
|
#### Testing Approach by Layer
|
||||||
|
|
||||||
|
- **Logic** - Pure state-based tests; no infrastructure needed
|
||||||
|
- **Infrastructure wrappers** - Narrow integration tests with real external systems
|
||||||
|
- **Application code** - Sociable tests with Nullable dependencies
|
||||||
|
- **LiveViews/Controllers** - Use Phoenix test helpers with test database
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
- Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps
|
- Use the already included and available `:req` (`Req`) library for HTTP requests, **avoid** `:httpoison`, `:tesla`, and `:httpc`. Req is included by default and is the preferred HTTP client for Phoenix apps
|
||||||
|
|
||||||
### Phoenix v1.8 guidelines
|
### Phoenix v1.8 guidelines
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue