ace-test 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ace-defaults/nav/protocols/agent-sources/ace-test.yml +19 -0
- data/.ace-defaults/nav/protocols/guide-sources/ace-test.yml +19 -0
- data/.ace-defaults/nav/protocols/tmpl-sources/ace-test.yml +11 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-test.yml +19 -0
- data/CHANGELOG.md +169 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +12 -0
- data/handbook/agents/mock.ag.md +164 -0
- data/handbook/agents/profile-tests.ag.md +132 -0
- data/handbook/agents/test.ag.md +99 -0
- data/handbook/guides/SUMMARY.md +95 -0
- data/handbook/guides/embedded-testing-guide.g.md +261 -0
- data/handbook/guides/mocking-patterns.g.md +464 -0
- data/handbook/guides/quick-reference.g.md +46 -0
- data/handbook/guides/test-driven-development-cycle/meta-documentation.md +26 -0
- data/handbook/guides/test-driven-development-cycle/ruby-application.md +18 -0
- data/handbook/guides/test-driven-development-cycle/ruby-gem.md +19 -0
- data/handbook/guides/test-driven-development-cycle/rust-cli.md +18 -0
- data/handbook/guides/test-driven-development-cycle/rust-wasm-zed.md +19 -0
- data/handbook/guides/test-driven-development-cycle/typescript-nuxt.md +18 -0
- data/handbook/guides/test-driven-development-cycle/typescript-vue.md +19 -0
- data/handbook/guides/test-layer-decision.g.md +261 -0
- data/handbook/guides/test-mocking-patterns.g.md +414 -0
- data/handbook/guides/test-organization.g.md +140 -0
- data/handbook/guides/test-performance.g.md +353 -0
- data/handbook/guides/test-responsibility-map.g.md +220 -0
- data/handbook/guides/test-review-checklist.g.md +231 -0
- data/handbook/guides/test-suite-health.g.md +337 -0
- data/handbook/guides/testable-code-patterns.g.md +315 -0
- data/handbook/guides/testing/ruby-rspec-config-examples.md +120 -0
- data/handbook/guides/testing/ruby-rspec.md +87 -0
- data/handbook/guides/testing/rust.md +52 -0
- data/handbook/guides/testing/test-maintenance.md +364 -0
- data/handbook/guides/testing/typescript-bun.md +47 -0
- data/handbook/guides/testing/vue-firebase-auth.md +546 -0
- data/handbook/guides/testing/vue-vitest.md +236 -0
- data/handbook/guides/testing-philosophy.g.md +82 -0
- data/handbook/guides/testing-strategy.g.md +151 -0
- data/handbook/guides/testing-tdd-cycle.g.md +146 -0
- data/handbook/guides/testing.g.md +170 -0
- data/handbook/skills/as-test-create-cases/SKILL.md +24 -0
- data/handbook/skills/as-test-fix/SKILL.md +26 -0
- data/handbook/skills/as-test-improve-coverage/SKILL.md +22 -0
- data/handbook/skills/as-test-optimize/SKILL.md +34 -0
- data/handbook/skills/as-test-performance-audit/SKILL.md +34 -0
- data/handbook/skills/as-test-plan/SKILL.md +34 -0
- data/handbook/skills/as-test-review/SKILL.md +34 -0
- data/handbook/skills/as-test-verify-suite/SKILL.md +45 -0
- data/handbook/templates/e2e-sandbox-checklist.template.md +289 -0
- data/handbook/templates/test-case.template.md +56 -0
- data/handbook/templates/test-performance-audit.template.md +132 -0
- data/handbook/templates/test-responsibility-map.template.md +92 -0
- data/handbook/templates/test-review-checklist.template.md +163 -0
- data/handbook/workflow-instructions/test/analyze-failures.wf.md +120 -0
- data/handbook/workflow-instructions/test/create-cases.wf.md +675 -0
- data/handbook/workflow-instructions/test/fix.wf.md +120 -0
- data/handbook/workflow-instructions/test/improve-coverage.wf.md +370 -0
- data/handbook/workflow-instructions/test/optimize.wf.md +368 -0
- data/handbook/workflow-instructions/test/performance-audit.wf.md +17 -0
- data/handbook/workflow-instructions/test/plan.wf.md +323 -0
- data/handbook/workflow-instructions/test/review.wf.md +16 -0
- data/handbook/workflow-instructions/test/verify-suite.wf.md +343 -0
- data/lib/ace/test/version.rb +7 -0
- data/lib/ace/test.rb +10 -0
- metadata +152 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: guide
|
|
3
|
+
title: Testable Code Patterns
|
|
4
|
+
purpose: Testable code design
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-01-23
|
|
7
|
+
last-checked: 2026-03-21
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Testable Code Patterns
|
|
11
|
+
|
|
12
|
+
## Avoiding Exit Calls in Testable Code
|
|
13
|
+
|
|
14
|
+
Commands that call `exit` will terminate the entire test process, preventing test completion and reporting. This manifests as:
|
|
15
|
+
- Test runner stops mid-execution
|
|
16
|
+
- No test summary is printed
|
|
17
|
+
- `ace-test` reports "0 tests, 0 assertions"
|
|
18
|
+
- Rake test fails with "Command failed with status (1)"
|
|
19
|
+
|
|
20
|
+
### Pattern: Return Status Codes in Commands
|
|
21
|
+
|
|
22
|
+
Commands should return status codes (0 for success, 1 for failure) and let the CLI entry point handle exit:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# BAD - Terminates test process
|
|
26
|
+
class MyCommand
|
|
27
|
+
def execute(args)
|
|
28
|
+
if args.include?("--help")
|
|
29
|
+
show_help
|
|
30
|
+
exit 0 # Kills tests!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
do_work
|
|
34
|
+
rescue => e
|
|
35
|
+
puts "Error: #{e.message}"
|
|
36
|
+
exit 1 # Kills tests!
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# GOOD - Returns status codes
|
|
41
|
+
class MyCommand
|
|
42
|
+
def execute(args)
|
|
43
|
+
if args.include?("--help")
|
|
44
|
+
show_help
|
|
45
|
+
return 0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
do_work
|
|
49
|
+
0
|
|
50
|
+
rescue => e
|
|
51
|
+
puts "Error: #{e.message}"
|
|
52
|
+
1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Pattern: Raise Exceptions in Organisms
|
|
58
|
+
|
|
59
|
+
Organisms (business logic) should raise exceptions instead of calling exit:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# BAD - Terminates test process
|
|
63
|
+
class IdeaWriter
|
|
64
|
+
def write(content, options)
|
|
65
|
+
if content.nil? || content.strip.empty?
|
|
66
|
+
puts "Error: No content provided"
|
|
67
|
+
exit 1 # Kills tests!
|
|
68
|
+
end
|
|
69
|
+
# ...
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# GOOD - Raises exceptions
|
|
74
|
+
class IdeaWriter
|
|
75
|
+
class IdeaWriterError < StandardError; end
|
|
76
|
+
|
|
77
|
+
def write(content, options)
|
|
78
|
+
if content.nil? || content.strip.empty?
|
|
79
|
+
raise IdeaWriterError, "No content provided"
|
|
80
|
+
end
|
|
81
|
+
# ...
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### CLI Entry Point Pattern
|
|
87
|
+
|
|
88
|
+
The CLI entry point (exe/ace-*) should handle status codes and exit only at the top level:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
#!/usr/bin/env ruby
|
|
92
|
+
# exe/ace-taskflow
|
|
93
|
+
|
|
94
|
+
require_relative "../lib/ace/taskflow"
|
|
95
|
+
|
|
96
|
+
# CLI.start returns status code
|
|
97
|
+
exit_code = Ace::Taskflow::CLI.start(ARGV)
|
|
98
|
+
exit(exit_code || 0)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
# lib/ace/taskflow/cli.rb
|
|
103
|
+
class CLI
|
|
104
|
+
def self.start(args)
|
|
105
|
+
case args.shift
|
|
106
|
+
when "retro"
|
|
107
|
+
require_relative "commands/retro_command"
|
|
108
|
+
Commands::RetroCommand.new.execute(args) # Returns status code
|
|
109
|
+
when "--help"
|
|
110
|
+
show_help
|
|
111
|
+
0 # Return status code
|
|
112
|
+
else
|
|
113
|
+
puts "Unknown command"
|
|
114
|
+
1 # Return status code
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Testing Commands with Status Codes
|
|
121
|
+
|
|
122
|
+
Test commands by asserting on their return values:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
def test_help_returns_success
|
|
126
|
+
output = capture_io do
|
|
127
|
+
exit_code = @command.execute(["--help"])
|
|
128
|
+
assert_equal 0, exit_code
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
assert_match(/Usage:/, output)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def test_error_returns_failure
|
|
135
|
+
output = capture_io do
|
|
136
|
+
exit_code = @command.execute(["invalid"])
|
|
137
|
+
assert_equal 1, exit_code
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
assert_match(/Error:/, output)
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Testing Organisms with Exceptions
|
|
145
|
+
|
|
146
|
+
Test organisms by asserting on raised exceptions:
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
def test_raises_error_on_invalid_input
|
|
150
|
+
error = assert_raises(Ace::Taskflow::Organisms::IdeaWriterError) do
|
|
151
|
+
@writer.write("")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
assert_match(/No content provided/, error.message)
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Migration Strategy
|
|
159
|
+
|
|
160
|
+
When refactoring commands with exit calls:
|
|
161
|
+
|
|
162
|
+
1. **Identify exit calls**: `grep -r "exit [01]" lib/`
|
|
163
|
+
2. **Refactor commands**: Replace `exit N` with `return N`
|
|
164
|
+
3. **Refactor organisms**: Replace `exit N` with `raise CustomError`
|
|
165
|
+
4. **Update CLI**: Return status codes, exit only at entry point
|
|
166
|
+
5. **Update tests**: Assert on return values instead of SystemExit
|
|
167
|
+
6. **Verify**: Run full test suite to ensure completion
|
|
168
|
+
|
|
169
|
+
### Benefits
|
|
170
|
+
|
|
171
|
+
1. **Test Completion**: Tests run to completion and report properly
|
|
172
|
+
2. **Better Debugging**: Exceptions provide stack traces
|
|
173
|
+
3. **Composability**: Commands can be called from other commands
|
|
174
|
+
4. **Isolation**: Test failures don't affect other tests
|
|
175
|
+
|
|
176
|
+
## Designing for Testability
|
|
177
|
+
|
|
178
|
+
### Explicit Dependencies
|
|
179
|
+
|
|
180
|
+
Pass dependencies (services, configurations, clients) into functions or classes rather than relying on global state or singletons. This makes mocking easier for unit tests.
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
# BAD - Hard to test, relies on global
|
|
184
|
+
class UserService
|
|
185
|
+
def create_user(params)
|
|
186
|
+
Database.connection.insert(:users, params)
|
|
187
|
+
EmailService.instance.send_welcome(params[:email])
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# GOOD - Dependencies are injectable
|
|
192
|
+
class UserService
|
|
193
|
+
def initialize(database:, email_service:)
|
|
194
|
+
@database = database
|
|
195
|
+
@email_service = email_service
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def create_user(params)
|
|
199
|
+
@database.insert(:users, params)
|
|
200
|
+
@email_service.send_welcome(params[:email])
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Pure Functions
|
|
206
|
+
|
|
207
|
+
Prefer functions that always return the same output for the same input and have no side effects. These are the easiest to test.
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
# PURE - Easy to test, deterministic
|
|
211
|
+
def calculate_discount(price, percentage)
|
|
212
|
+
price * (percentage / 100.0)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# IMPURE - Hard to test, depends on external state
|
|
216
|
+
def calculate_discount(product_id)
|
|
217
|
+
product = Product.find(product_id)
|
|
218
|
+
today_rate = DiscountService.current_rate
|
|
219
|
+
product.price * today_rate
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Separate Logic from I/O
|
|
224
|
+
|
|
225
|
+
Isolate core business logic from operations that interact with external systems (files, network, databases). Test the logic separately from the I/O.
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
# MIXED - Hard to test
|
|
229
|
+
class ReportGenerator
|
|
230
|
+
def generate
|
|
231
|
+
data = File.read("input.csv")
|
|
232
|
+
results = CSV.parse(data).map { |row| transform(row) }
|
|
233
|
+
File.write("output.json", results.to_json)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# SEPARATED - Logic is testable independently
|
|
238
|
+
class ReportGenerator
|
|
239
|
+
def generate(input_data)
|
|
240
|
+
CSV.parse(input_data).map { |row| transform(row) }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# I/O handled separately
|
|
245
|
+
class ReportService
|
|
246
|
+
def run(input_path, output_path)
|
|
247
|
+
data = File.read(input_path)
|
|
248
|
+
results = ReportGenerator.new.generate(data)
|
|
249
|
+
File.write(output_path, results.to_json)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Clear Interfaces
|
|
255
|
+
|
|
256
|
+
Define clear, well-documented public interfaces for modules and classes. This clarifies boundaries for testing and for AI interaction.
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
# Clear interface with documented contract
|
|
260
|
+
class PaymentProcessor
|
|
261
|
+
# @param amount [Decimal] Amount to charge
|
|
262
|
+
# @param card_token [String] Payment card token
|
|
263
|
+
# @return [PaymentResult] Result with success/failure status
|
|
264
|
+
def charge(amount, card_token)
|
|
265
|
+
# ...
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Stateless Components
|
|
271
|
+
|
|
272
|
+
Where possible, favor stateless components or functions, as state management adds complexity to testing.
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
# STATEFUL - Harder to test, order matters
|
|
276
|
+
class Counter
|
|
277
|
+
def initialize
|
|
278
|
+
@count = 0
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def increment
|
|
282
|
+
@count += 1
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def current
|
|
286
|
+
@count
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# STATELESS - Easy to test, no hidden state
|
|
291
|
+
def increment(count)
|
|
292
|
+
count + 1
|
|
293
|
+
end
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Summary
|
|
297
|
+
|
|
298
|
+
### Core Principles
|
|
299
|
+
- Extract external dependencies to protected methods
|
|
300
|
+
- Use method stubbing instead of subprocess isolation
|
|
301
|
+
- Profile tests regularly with `ace-test --profile 10`
|
|
302
|
+
- Only use subprocesses when true process isolation is required
|
|
303
|
+
|
|
304
|
+
### Testability Patterns
|
|
305
|
+
- **Never call exit in commands or organisms** - return status codes and raise exceptions
|
|
306
|
+
- **Handle exit only at the CLI entry point** (exe/ace-*)
|
|
307
|
+
- **Return status codes from commands**, let CLI handle exit
|
|
308
|
+
- **Inject dependencies** instead of using globals
|
|
309
|
+
- **Separate logic from I/O** for easier testing
|
|
310
|
+
|
|
311
|
+
## Related Guides
|
|
312
|
+
|
|
313
|
+
- [Testing Philosophy](guide://testing-philosophy) - Why testability matters
|
|
314
|
+
- [Mocking Patterns](guide://mocking-patterns) - How to stub dependencies
|
|
315
|
+
- [Test Performance](guide://test-performance) - Fast test execution
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: guide
|
|
3
|
+
title: Ruby RSpec Configuration Examples
|
|
4
|
+
purpose: RSpec configuration reference
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-01-23
|
|
7
|
+
last-checked: 2026-03-21
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ruby RSpec Configuration Examples
|
|
11
|
+
|
|
12
|
+
## Testing Framework Setup
|
|
13
|
+
|
|
14
|
+
### 1. RSpec Configuration
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
# spec/spec_helper.rb
|
|
18
|
+
require 'rspec'
|
|
19
|
+
require 'faker'
|
|
20
|
+
require 'vcr'
|
|
21
|
+
require 'webmock/rspec'
|
|
22
|
+
|
|
23
|
+
RSpec.configure do |config|
|
|
24
|
+
config.before(:suite) do
|
|
25
|
+
# Setup test environment
|
|
26
|
+
Aira.env = :test
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
config.around(:each) do |example|
|
|
30
|
+
# Clear any cached data
|
|
31
|
+
Aira.reset!
|
|
32
|
+
example.run
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Mock external services
|
|
37
|
+
VCR.configure do |config|
|
|
38
|
+
config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
|
|
39
|
+
config.hook_into :webmock
|
|
40
|
+
config.configure_rspec_metadata!
|
|
41
|
+
|
|
42
|
+
# Filter sensitive data
|
|
43
|
+
config.filter_sensitive_data('<API_KEY>') { ENV['API_KEY'] }
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Test Data Management
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# spec/factories/prompts.rb
|
|
51
|
+
FactoryBot.define do
|
|
52
|
+
factory :prompt, class: Hash do
|
|
53
|
+
content { Faker::Lorem.paragraph }
|
|
54
|
+
temperature { rand(0.0..1.0) }
|
|
55
|
+
|
|
56
|
+
trait :with_context do
|
|
57
|
+
context { { role: "user", history: [] } }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
initialize_with { attributes }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# spec/fixtures/test_data.yml
|
|
65
|
+
prompts:
|
|
66
|
+
basic:
|
|
67
|
+
content: "What is 2+2?"
|
|
68
|
+
temperature: 0.7
|
|
69
|
+
complex:
|
|
70
|
+
content: "Analyze this code..."
|
|
71
|
+
temperature: 0.9
|
|
72
|
+
max_tokens: 1000
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Integration Testing
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
RSpec.describe "Agent Integration", :integration do
|
|
79
|
+
let(:agent) { Aira::Agent.new }
|
|
80
|
+
let(:tool) { Aira::Tools::Calculator.new }
|
|
81
|
+
|
|
82
|
+
around(:each) do |example|
|
|
83
|
+
VCR.use_cassette("integration_#{example.description}") do
|
|
84
|
+
example.run
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "executes complete workflow" do
|
|
89
|
+
agent.add_tool(tool)
|
|
90
|
+
result = agent.execute(
|
|
91
|
+
prompt: build(:prompt, :with_context),
|
|
92
|
+
max_retries: 2
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
expect(result).to be_successful
|
|
96
|
+
expect(result.output).to include("calculation result")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Performance Testing
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
RSpec.describe "Performance", :performance do
|
|
105
|
+
include PerformanceHelpers
|
|
106
|
+
|
|
107
|
+
it "handles concurrent requests within threshold" do
|
|
108
|
+
measure_concurrent_execution(threads: 10) do |stats|
|
|
109
|
+
expect(stats.average_response_time).to be < 1.0
|
|
110
|
+
expect(stats.error_rate).to be < 0.01
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "manages memory usage" do
|
|
115
|
+
measure_memory_usage do |stats|
|
|
116
|
+
expect(stats.peak_memory_mb).to be < 200
|
|
117
|
+
expect(stats.memory_growth_mb).to be < 10
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: guide
|
|
3
|
+
title: Ruby RSpec Testing Guide
|
|
4
|
+
purpose: Ruby RSpec testing conventions
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-01-23
|
|
7
|
+
last-checked: 2026-03-21
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Ruby RSpec Testing Guide
|
|
11
|
+
|
|
12
|
+
This guide provides best practices and conventions for writing tests in Ruby using RSpec, tailored for the
|
|
13
|
+
coding-agent-workflow-toolkit project.
|
|
14
|
+
|
|
15
|
+
## 1. Directory Structure
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
project-root/
|
|
19
|
+
└── spec/
|
|
20
|
+
├── unit/
|
|
21
|
+
├── integration/
|
|
22
|
+
├── e2e/
|
|
23
|
+
└── support/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Follow the mirrored structure of `lib/` when placing unit tests. Keep integration and E2E tests in their respective
|
|
27
|
+
folders.
|
|
28
|
+
|
|
29
|
+
## 2. Configuration (`spec/spec_helper.rb`)
|
|
30
|
+
|
|
31
|
+
- Load `simplecov` when the `COVERAGE` env var is present.
|
|
32
|
+
- Enable the `--only-failures` and `--next-failure` flags via `.rspec`.
|
|
33
|
+
- Configure `rspec-mocks` to verify double constants.
|
|
34
|
+
|
|
35
|
+
## 3. Factories & Fixtures
|
|
36
|
+
|
|
37
|
+
- Use `FactoryBot` for building domain objects.
|
|
38
|
+
- Place YAML/JSON fixtures in `spec/fixtures/`.
|
|
39
|
+
|
|
40
|
+
## 4. Mocking External Services
|
|
41
|
+
|
|
42
|
+
- Use `WebMock` and `VCR` to record HTTP interactions.
|
|
43
|
+
- Filter sensitive data with `config.filter_sensitive_data`.
|
|
44
|
+
|
|
45
|
+
## 5. Tag Conventions
|
|
46
|
+
|
|
47
|
+
| Tag | Purpose |
|
|
48
|
+
| --- | ------- |
|
|
49
|
+
| `:unit` | Fast unit tests (default) |
|
|
50
|
+
| `:integration` | Tests requiring multiple components |
|
|
51
|
+
| `:e2e` | End-to-end flows |
|
|
52
|
+
| `:slow` | Any test > 1s |
|
|
53
|
+
|
|
54
|
+
## 6. Running Tests
|
|
55
|
+
|
|
56
|
+
Typically, you run your RSpec tests using Bundler to ensure the correct gem versions are used:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Run all specs
|
|
60
|
+
bundle exec rspec
|
|
61
|
+
|
|
62
|
+
# Run specs in a specific file
|
|
63
|
+
bundle exec rspec spec/models/user_spec.rb
|
|
64
|
+
|
|
65
|
+
# Run a specific example (by line number)
|
|
66
|
+
bundle exec rspec spec/models/user_spec.rb:25
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 7. Checking Test Coverage
|
|
70
|
+
|
|
71
|
+
If you have set up a coverage tool like SimpleCov, you might run it via an environment variable:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Run specs and generate coverage report
|
|
75
|
+
COVERAGE=true bundle exec rspec
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Refer to your project's specific setup (e.g., `spec_helper.rb` or `rails_helper.rb`) and the coverage tool's
|
|
79
|
+
documentation for configuration details.
|
|
80
|
+
|
|
81
|
+
## 8. CI Integration
|
|
82
|
+
|
|
83
|
+
- Ensure the CI pipeline runs `bundle exec rspec` and uploads coverage.
|
|
84
|
+
|
|
85
|
+
## 9. Linting Specs
|
|
86
|
+
|
|
87
|
+
Use `rubocop-rspec` to enforce spec style.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: guide
|
|
3
|
+
title: Rust Testing Guide
|
|
4
|
+
purpose: Rust testing conventions
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-01-23
|
|
7
|
+
last-checked: 2026-03-21
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Rust Testing Guide
|
|
11
|
+
|
|
12
|
+
This guide provides best practices for testing Rust code in the context of the coding-agent-workflow-toolkit project.
|
|
13
|
+
|
|
14
|
+
## 1. Test Types
|
|
15
|
+
|
|
16
|
+
- **Unit Tests**: Inline `#[cfg(test)]` modules next to implementation.
|
|
17
|
+
- **Integration Tests**: Files in `tests/` directory.
|
|
18
|
+
- **Doc Tests**: Ensure examples in doc comments compile and run.
|
|
19
|
+
|
|
20
|
+
## 2. Directory Layout
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
project-root/
|
|
24
|
+
├── src/
|
|
25
|
+
│ └── lib.rs
|
|
26
|
+
├── tests/
|
|
27
|
+
│ └── integration.rs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 3. Running Tests
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cargo test # all tests
|
|
34
|
+
cargo test my_test # specific
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use `-- --nocapture` to see stdout.
|
|
38
|
+
|
|
39
|
+
## 4. Mocking & Fakes
|
|
40
|
+
|
|
41
|
+
- Use crates like `mockall` for trait-based mocking.
|
|
42
|
+
- For HTTP interactions, use `wiremock` crate.
|
|
43
|
+
|
|
44
|
+
## 5. Coverage
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cargo tarpaulin --out Html
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 6. CI Integration
|
|
51
|
+
|
|
52
|
+
Ensure job uses `cargo test --all --locked`.
|