Skip to content

Commit ac567d7

Browse files
authored
feat: Comprehensive usage-rules.md for easy synchronisation of coding agents (#102)
1 parent 5d97731 commit ac567d7

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed

usage-rules.md

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Usage Rules for Mimic Library
2+
3+
This document provides essential guidelines for coding agents when using the Mimic mocking library in Elixir projects. You should not mock any module that are part of the Elixir standard library nor the Otp library directly.
4+
5+
## Critical Setup Requirements
6+
7+
### **IMPORTANT: Module Preparation**
8+
9+
- **MUST** call `Mimic.copy/2` for each module you want to mock in `test_helper.exs`
10+
- IMPORTANT: Always call Mimic.copy/2 with type_check: true. Never omit it.
11+
- **IMPORTANT**: Call `copy/2` BEFORE `ExUnit.start()`
12+
- Copying a module does NOT change its behavior until you stub/expect it
13+
14+
```elixir
15+
# test_helper.exs
16+
Mimic.copy(Calculator, type_check: true)
17+
Mimic.copy(HTTPClient, type_check: true)
18+
ExUnit.start()
19+
```
20+
21+
### **IMPORTANT: Test Module Setup**
22+
23+
- **MUST** use `use Mimic` or `use Mimic.DSL` in test modules
24+
- **IMPORTANT**: Include `:test` environment only in mix.exs dependency
25+
26+
```elixir
27+
# In test files
28+
defmodule MyTest do
29+
use ExUnit.Case, async: true
30+
use Mimic # or use Mimic.DSL
31+
32+
# tests here
33+
end
34+
```
35+
36+
## Core API Functions (Order by Importance)
37+
38+
### 1. `expect/4` - Primary Testing Function
39+
40+
**IMPORTANT**: Use for functions that MUST be called during test
41+
42+
- Creates expectations that must be fulfilled or test fails
43+
- Works like a FIFO queue for multiple calls
44+
- Auto-verified at test end when using `use Mimic`
45+
46+
```elixir
47+
# Single call expectation
48+
Calculator
49+
|> expect(:add, fn x, y -> x + y end)
50+
51+
# Multiple calls expectation
52+
Calculator
53+
|> expect(:add, 3, fn x, y -> x + y end)
54+
55+
# Chaining expectations (FIFO order)
56+
Calculator
57+
|> expect(:add, fn _, _ -> :first_call end)
58+
|> expect(:add, fn _, _ -> :second_call end)
59+
```
60+
61+
### 2. `stub/3` - Flexible Mock Replacement
62+
63+
- Use for functions that MAY be called during test
64+
- No verification failure if not called
65+
- Can be called multiple times
66+
67+
```elixir
68+
Calculator
69+
|> stub(:add, fn x, y -> x + y end)
70+
```
71+
72+
### 3. `stub/1` - Complete Module Stubbing
73+
74+
- Stubs ALL public functions in module
75+
- Stubbed functions raise `UnexpectedCallError` when called
76+
77+
```elixir
78+
stub(Calculator) # All functions will raise if called
79+
```
80+
81+
### 4. `reject/1` or `reject/3` - Forbidden Calls
82+
83+
- Use to ensure functions are NOT called
84+
- Test fails if rejected function is called
85+
86+
```elixir
87+
reject(&Calculator.dangerous_operation/1)
88+
# or
89+
reject(Calculator, :dangerous_operation, 1)
90+
```
91+
92+
## Mode Selection (Critical Decision)
93+
94+
### **IMPORTANT: Choose Appropriate Mode**
95+
96+
#### Private Mode (Default - Recommended)
97+
98+
- Tests can run with `async: true`
99+
- Each process sees its own mocks
100+
- Use `allow/3` for multi-process scenarios
101+
102+
#### Global Mode (Use Sparingly)
103+
104+
- **IMPORTANT**: Use `setup :set_mimic_global`
105+
- **CRITICAL**: MUST use `async: false` in global mode
106+
- All processes see same mocks
107+
- Only global owner can create stubs/expectations
108+
109+
```elixir
110+
# Private mode (preferred)
111+
setup :set_mimic_private
112+
setup :verify_on_exit!
113+
114+
# Global mode (when needed)
115+
setup :set_mimic_global
116+
# Remember: async: false required
117+
```
118+
119+
## DSL Mode Alternative
120+
121+
### **IMPORTANT: DSL Syntax**
122+
123+
Use `Mimic.DSL` for more natural syntax:
124+
125+
```elixir
126+
use Mimic.DSL
127+
128+
test "DSL example" do
129+
stub Calculator.add(_x, _y), do: :stubbed
130+
expect Calculator.mult(x, y), do: x * y
131+
expect Calculator.add(x, y), num_calls: 2, do: x + y
132+
end
133+
```
134+
135+
## Multi-Process Coordination
136+
137+
### Using `allow/3` (Private Mode)
138+
139+
```elixir
140+
test "multi-process test" do
141+
Calculator |> expect(:add, fn x, y -> x + y end)
142+
143+
parent_pid = self()
144+
145+
spawn_link(fn ->
146+
Calculator |> allow(parent_pid, self())
147+
assert Calculator.add(1, 2) == 3
148+
end)
149+
end
150+
```
151+
152+
### **IMPORTANT**: Task Automatic Allowance
153+
154+
- Tasks automatically inherit parent process mocks
155+
- No need to call `allow/3` for `Task.async`
156+
157+
## Critical Don'ts
158+
159+
### **IMPORTANT: Function Export Requirements**
160+
161+
- Can ONLY mock publicly exported functions
162+
- **MUST** match exact arity
163+
- Will raise `ArgumentError` for non-existent functions
164+
165+
### **IMPORTANT: Intra-Module Function Calls**
166+
167+
- Mocking does NOT work for internal function calls within same module
168+
- Use fully qualified names (`Module.function`) instead of local calls
169+
170+
### **IMPORTANT: Global Mode Restrictions**
171+
172+
- Only global owner process can create stubs/expectations
173+
- Other processes will get `ArgumentError`
174+
- Cannot use `allow/3` in global mode
175+
176+
## Advanced Features
177+
178+
### Type Checking (Experimental)
179+
180+
```elixir
181+
Mimic.copy(HTTPClient, type_check: true)
182+
```
183+
184+
### Calling Original Implementation
185+
186+
```elixir
187+
call_original(Calculator, :add, [1, 2]) # Returns 3
188+
```
189+
190+
### Tracking Function Calls
191+
192+
```elixir
193+
stub(Calculator, :add, fn x, y -> x + y end)
194+
Calculator.add(1, 2)
195+
calls(&Calculator.add/2) # Returns [[1, 2]]
196+
```
197+
198+
### Fake Module Stubbing
199+
200+
```elixir
201+
stub_with(Calculator, MockCalculator) # Replace all functions
202+
```
203+
204+
## Common Patterns
205+
206+
### Setup Pattern
207+
208+
```elixir
209+
setup do
210+
# Common setup
211+
%{user: %User{id: 1}}
212+
end
213+
214+
setup :verify_on_exit! # Auto-verify expectations
215+
```
216+
217+
### Expectation Chaining
218+
219+
```elixir
220+
Calculator
221+
|> stub(:add, fn _, _ -> :fallback end) # Fallback after expectations
222+
|> expect(:add, fn _, _ -> :first end) # First call
223+
|> expect(:add, fn _, _ -> :second end) # Second call
224+
# Third call returns :fallback
225+
```
226+
227+
## Error Handling
228+
229+
### Common Errors and Solutions
230+
231+
- `Module X has not been copied` → Add `Mimic.copy(X)` to test_helper.exs
232+
- `Function not defined for Module` → Check function name/arity
233+
- `Only the global owner is allowed` → Wrong process in global mode
234+
- `Allow must not be called when mode is global` → Don't mix allow with global mode
235+
236+
**IMPORTANT**: Always verify exact function signatures and ensure modules are properly copied before mocking.

0 commit comments

Comments
 (0)