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
|
||||
- **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
|
||||
|
||||
### 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
|
||||
|
||||
### Phoenix v1.8 guidelines
|
||||
|
|
|
|||
Loading…
Reference in a new issue