agy-superpowers 5.2.2 → 5.2.4
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.
- package/README.md +47 -150
- package/package.json +1 -1
- package/template/agent/rules/CLAUDE.md +80 -0
- package/template/agent/rules/code-styles.md +31 -32
- package/template/agent/rules/debug-confirmation-policy.md +2 -0
- package/template/agent/rules/file-length-policy.md +2 -0
- package/template/agent/rules/git-policy.md +7 -0
- package/template/agent/rules/language-matching.md +2 -0
- package/template/agent/rules/scratch-scripts.md +39 -0
- package/template/agent/rules/superpowers.md +8 -51
- package/template/agent/skills/executing-plans/SKILL.md +17 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +16 -0
- package/template/agent/skills/test-driven-development/SKILL.md +16 -0
- package/template/agent/skills/verification-before-completion/SKILL.md +22 -0
- package/template/agent/skills/writing-plans/SKILL.md +16 -0
- package/template/agent/skills/ai-integrated-product/SKILL.md +0 -57
- package/template/agent/skills/analytics-setup/SKILL.md +0 -51
- package/template/agent/skills/api-design/SKILL.md +0 -193
- package/template/agent/skills/app-store-optimizer/SKILL.md +0 -127
- package/template/agent/skills/auth-and-identity/SKILL.md +0 -167
- package/template/agent/skills/backend-developer/SKILL.md +0 -148
- package/template/agent/skills/bootstrapper-finance/SKILL.md +0 -55
- package/template/agent/skills/chrome-extension-developer/SKILL.md +0 -53
- package/template/agent/skills/community-manager/SKILL.md +0 -115
- package/template/agent/skills/content-marketer/SKILL.md +0 -111
- package/template/agent/skills/conversion-optimizer/SKILL.md +0 -142
- package/template/agent/skills/cto-architect/SKILL.md +0 -133
- package/template/agent/skills/customer-success-manager/SKILL.md +0 -126
- package/template/agent/skills/data-analyst/SKILL.md +0 -147
- package/template/agent/skills/devops-engineer/SKILL.md +0 -117
- package/template/agent/skills/email-infrastructure/SKILL.md +0 -164
- package/template/agent/skills/game-design/SKILL.md +0 -194
- package/template/agent/skills/game-developer/SKILL.md +0 -175
- package/template/agent/skills/growth-hacker/SKILL.md +0 -122
- package/template/agent/skills/idea-validator/SKILL.md +0 -55
- package/template/agent/skills/indie-legal/SKILL.md +0 -53
- package/template/agent/skills/influencer-marketer/SKILL.md +0 -141
- package/template/agent/skills/landing-page-builder/SKILL.md +0 -59
- package/template/agent/skills/launch-strategist/SKILL.md +0 -62
- package/template/agent/skills/market-researcher/SKILL.md +0 -53
- package/template/agent/skills/micro-saas-builder/SKILL.md +0 -56
- package/template/agent/skills/monetization-strategist/SKILL.md +0 -119
- package/template/agent/skills/paid-acquisition-specialist/SKILL.md +0 -119
- package/template/agent/skills/pricing-psychologist/SKILL.md +0 -58
- package/template/agent/skills/real-time-features/SKILL.md +0 -194
- package/template/agent/skills/retention-specialist/SKILL.md +0 -123
- package/template/agent/skills/rust-developer/SKILL.md +0 -281
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +0 -231
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +0 -132
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +0 -121
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +0 -165
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +0 -163
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +0 -125
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +0 -177
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +0 -184
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +0 -182
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +0 -199
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +0 -185
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +0 -203
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +0 -195
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +0 -191
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +0 -198
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +0 -169
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +0 -116
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +0 -128
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +0 -179
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +0 -145
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +0 -115
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +0 -107
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +0 -157
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +0 -158
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +0 -147
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +0 -174
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +0 -138
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +0 -156
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +0 -172
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +0 -164
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +0 -99
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +0 -104
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +0 -94
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +0 -78
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +0 -76
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +0 -129
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +0 -131
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +0 -86
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +0 -118
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +0 -92
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +0 -101
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +0 -187
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +0 -152
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +0 -181
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +0 -167
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +0 -141
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +0 -95
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +0 -124
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +0 -105
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +0 -65
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +0 -97
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +0 -122
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +0 -119
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +0 -153
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +0 -136
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +0 -134
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +0 -150
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +0 -123
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +0 -113
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +0 -175
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +0 -149
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +0 -133
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +0 -148
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +0 -120
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +0 -155
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +0 -139
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +0 -135
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +0 -186
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +0 -162
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +0 -171
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +0 -168
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +0 -151
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +0 -189
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +0 -226
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +0 -161
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +0 -130
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +0 -127
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +0 -154
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +0 -142
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +0 -146
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +0 -160
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +0 -159
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +0 -144
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +0 -137
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +0 -188
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +0 -143
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +0 -131
- package/template/agent/skills/saas-architect/SKILL.md +0 -139
- package/template/agent/skills/security-engineer/SKILL.md +0 -133
- package/template/agent/skills/seo-specialist/SKILL.md +0 -130
- package/template/agent/skills/solo-founder-ops/SKILL.md +0 -56
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# test-mock-traits
|
|
2
|
-
|
|
3
|
-
> Use traits for dependencies to enable mocking in tests
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Concrete dependencies make testing hard—you can't easily test error paths, timeouts, or edge cases without real external systems. Extracting dependencies behind traits lets you inject test doubles (mocks, fakes, stubs), enabling isolated unit tests that run fast and cover edge cases.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
struct UserService {
|
|
13
|
-
db: PostgresConnection, // Concrete type - hard to test
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
impl UserService {
|
|
17
|
-
async fn get_user(&self, id: u64) -> Result<User, Error> {
|
|
18
|
-
// Directly calls Postgres - needs real database to test
|
|
19
|
-
self.db.query("SELECT * FROM users WHERE id = $1", &[&id]).await
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Test requires real Postgres instance
|
|
24
|
-
#[tokio::test]
|
|
25
|
-
async fn test_get_user() {
|
|
26
|
-
let db = PostgresConnection::connect("postgres://...").await?;
|
|
27
|
-
let service = UserService { db };
|
|
28
|
-
// Slow, flaky, can't test error paths
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Good
|
|
33
|
-
|
|
34
|
-
```rust
|
|
35
|
-
// Define trait for dependency
|
|
36
|
-
#[async_trait]
|
|
37
|
-
trait UserRepository: Send + Sync {
|
|
38
|
-
async fn find_by_id(&self, id: u64) -> Result<Option<User>, DbError>;
|
|
39
|
-
async fn save(&self, user: &User) -> Result<(), DbError>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Production implementation
|
|
43
|
-
struct PostgresUserRepo {
|
|
44
|
-
pool: PgPool,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
#[async_trait]
|
|
48
|
-
impl UserRepository for PostgresUserRepo {
|
|
49
|
-
async fn find_by_id(&self, id: u64) -> Result<Option<User>, DbError> {
|
|
50
|
-
sqlx::query_as("SELECT * FROM users WHERE id = $1")
|
|
51
|
-
.bind(id)
|
|
52
|
-
.fetch_optional(&self.pool)
|
|
53
|
-
.await
|
|
54
|
-
}
|
|
55
|
-
// ...
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Service depends on trait, not concrete type
|
|
59
|
-
struct UserService<R: UserRepository> {
|
|
60
|
-
repo: R,
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
impl<R: UserRepository> UserService<R> {
|
|
64
|
-
async fn get_user(&self, id: u64) -> Result<User, Error> {
|
|
65
|
-
self.repo.find_by_id(id).await?
|
|
66
|
-
.ok_or(Error::NotFound)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Test with mock
|
|
71
|
-
#[cfg(test)]
|
|
72
|
-
mod tests {
|
|
73
|
-
struct MockUserRepo {
|
|
74
|
-
users: HashMap<u64, User>,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
#[async_trait]
|
|
78
|
-
impl UserRepository for MockUserRepo {
|
|
79
|
-
async fn find_by_id(&self, id: u64) -> Result<Option<User>, DbError> {
|
|
80
|
-
Ok(self.users.get(&id).cloned())
|
|
81
|
-
}
|
|
82
|
-
// ...
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
#[tokio::test]
|
|
86
|
-
async fn test_get_user_found() {
|
|
87
|
-
let mut mock = MockUserRepo { users: HashMap::new() };
|
|
88
|
-
mock.users.insert(1, User { id: 1, name: "Alice".into() });
|
|
89
|
-
|
|
90
|
-
let service = UserService { repo: mock };
|
|
91
|
-
let user = service.get_user(1).await.unwrap();
|
|
92
|
-
|
|
93
|
-
assert_eq!(user.name, "Alice");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
#[tokio::test]
|
|
97
|
-
async fn test_get_user_not_found() {
|
|
98
|
-
let mock = MockUserRepo { users: HashMap::new() };
|
|
99
|
-
let service = UserService { repo: mock };
|
|
100
|
-
|
|
101
|
-
let result = service.get_user(999).await;
|
|
102
|
-
assert!(matches!(result, Err(Error::NotFound)));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## mockall Crate
|
|
108
|
-
|
|
109
|
-
```rust
|
|
110
|
-
use mockall::*;
|
|
111
|
-
use mockall::predicate::*;
|
|
112
|
-
|
|
113
|
-
#[automock]
|
|
114
|
-
#[async_trait]
|
|
115
|
-
trait Database: Send + Sync {
|
|
116
|
-
async fn query(&self, sql: &str) -> Result<Vec<Row>, Error>;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
#[tokio::test]
|
|
120
|
-
async fn test_with_mockall() {
|
|
121
|
-
let mut mock = MockDatabase::new();
|
|
122
|
-
|
|
123
|
-
mock.expect_query()
|
|
124
|
-
.with(eq("SELECT 1"))
|
|
125
|
-
.times(1)
|
|
126
|
-
.returning(|_| Ok(vec![Row::new()]));
|
|
127
|
-
|
|
128
|
-
let result = mock.query("SELECT 1").await;
|
|
129
|
-
assert!(result.is_ok());
|
|
130
|
-
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Testing Error Paths
|
|
134
|
-
|
|
135
|
-
```rust
|
|
136
|
-
#[async_trait]
|
|
137
|
-
trait HttpClient: Send + Sync {
|
|
138
|
-
async fn get(&self, url: &str) -> Result<Response, HttpError>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
struct FailingClient;
|
|
142
|
-
|
|
143
|
-
#[async_trait]
|
|
144
|
-
impl HttpClient for FailingClient {
|
|
145
|
-
async fn get(&self, _url: &str) -> Result<Response, HttpError> {
|
|
146
|
-
Err(HttpError::Timeout) // Always fails
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
#[tokio::test]
|
|
151
|
-
async fn test_handles_timeout() {
|
|
152
|
-
let client = FailingClient;
|
|
153
|
-
let service = ApiService { client };
|
|
154
|
-
|
|
155
|
-
let result = service.fetch_data().await;
|
|
156
|
-
assert!(matches!(result, Err(Error::NetworkError(_))));
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Dynamic Dispatch Alternative
|
|
161
|
-
|
|
162
|
-
```rust
|
|
163
|
-
// When you don't want generics everywhere
|
|
164
|
-
struct UserService {
|
|
165
|
-
repo: Box<dyn UserRepository>,
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
impl UserService {
|
|
169
|
-
fn new(repo: impl UserRepository + 'static) -> Self {
|
|
170
|
-
Self { repo: Box::new(repo) }
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Slight runtime cost but cleaner API
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Cargo.toml
|
|
178
|
-
|
|
179
|
-
```toml
|
|
180
|
-
[dev-dependencies]
|
|
181
|
-
mockall = "0.11"
|
|
182
|
-
async-trait = "0.1" # For async trait mocking
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## See Also
|
|
186
|
-
|
|
187
|
-
- [api-sealed-trait](./api-sealed-trait.md) - Trait design
|
|
188
|
-
- [test-proptest-properties](./test-proptest-properties.md) - Property-based testing
|
|
189
|
-
- [proj-lib-main-split](./proj-lib-main-split.md) - Testable architecture
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
# test-mockall-mocking
|
|
2
|
-
|
|
3
|
-
> Use mockall for trait mocking
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Unit tests should isolate the code under test from external dependencies (databases, APIs, file systems). Mockall generates mock implementations of traits, allowing you to control and verify behavior without real dependencies.
|
|
8
|
-
|
|
9
|
-
## Setup
|
|
10
|
-
|
|
11
|
-
```toml
|
|
12
|
-
# Cargo.toml
|
|
13
|
-
[dev-dependencies]
|
|
14
|
-
mockall = "0.12"
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Basic Usage
|
|
18
|
-
|
|
19
|
-
```rust
|
|
20
|
-
use mockall::automock;
|
|
21
|
-
|
|
22
|
-
#[automock]
|
|
23
|
-
trait Database {
|
|
24
|
-
fn get_user(&self, id: u64) -> Option<User>;
|
|
25
|
-
fn save_user(&self, user: &User) -> Result<(), Error>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
#[cfg(test)]
|
|
29
|
-
mod tests {
|
|
30
|
-
use super::*;
|
|
31
|
-
use mockall::predicate::*;
|
|
32
|
-
|
|
33
|
-
#[test]
|
|
34
|
-
fn test_get_user() {
|
|
35
|
-
let mut mock = MockDatabase::new();
|
|
36
|
-
|
|
37
|
-
mock.expect_get_user()
|
|
38
|
-
.with(eq(42))
|
|
39
|
-
.returning(|_| Some(User { id: 42, name: "Alice".into() }));
|
|
40
|
-
|
|
41
|
-
let service = UserService::new(mock);
|
|
42
|
-
let user = service.find_user(42);
|
|
43
|
-
|
|
44
|
-
assert_eq!(user.unwrap().name, "Alice");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Expectations
|
|
50
|
-
|
|
51
|
-
```rust
|
|
52
|
-
#[cfg(test)]
|
|
53
|
-
mod tests {
|
|
54
|
-
use super::*;
|
|
55
|
-
|
|
56
|
-
#[test]
|
|
57
|
-
fn test_save_calls() {
|
|
58
|
-
let mut mock = MockDatabase::new();
|
|
59
|
-
|
|
60
|
-
// Expect exactly one call
|
|
61
|
-
mock.expect_save_user()
|
|
62
|
-
.times(1)
|
|
63
|
-
.returning(|_| Ok(()));
|
|
64
|
-
|
|
65
|
-
// Expect call with specific argument
|
|
66
|
-
mock.expect_get_user()
|
|
67
|
-
.with(eq(42))
|
|
68
|
-
.returning(|_| Some(User::default()));
|
|
69
|
-
|
|
70
|
-
// Expect multiple calls
|
|
71
|
-
mock.expect_get_user()
|
|
72
|
-
.times(3..) // At least 3 times
|
|
73
|
-
.returning(|_| None);
|
|
74
|
-
|
|
75
|
-
// Expectations are verified on drop
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Predicates
|
|
81
|
-
|
|
82
|
-
```rust
|
|
83
|
-
use mockall::predicate::*;
|
|
84
|
-
|
|
85
|
-
mock.expect_process()
|
|
86
|
-
.with(eq(42)) // Exact match
|
|
87
|
-
.returning(|_| Ok(()));
|
|
88
|
-
|
|
89
|
-
mock.expect_validate()
|
|
90
|
-
.with(function(|s: &str| s.len() > 5)) // Custom predicate
|
|
91
|
-
.returning(|_| true);
|
|
92
|
-
|
|
93
|
-
mock.expect_search()
|
|
94
|
-
.withf(|query, limit| { // Multiple args
|
|
95
|
-
query.len() < 100 && *limit <= 1000
|
|
96
|
-
})
|
|
97
|
-
.returning(|_, _| vec![]);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Sequences
|
|
101
|
-
|
|
102
|
-
```rust
|
|
103
|
-
use mockall::Sequence;
|
|
104
|
-
|
|
105
|
-
#[test]
|
|
106
|
-
fn test_ordered_calls() {
|
|
107
|
-
let mut seq = Sequence::new();
|
|
108
|
-
let mut mock = MockDatabase::new();
|
|
109
|
-
|
|
110
|
-
mock.expect_connect()
|
|
111
|
-
.times(1)
|
|
112
|
-
.in_sequence(&mut seq)
|
|
113
|
-
.returning(|| Ok(()));
|
|
114
|
-
|
|
115
|
-
mock.expect_query()
|
|
116
|
-
.times(1)
|
|
117
|
-
.in_sequence(&mut seq)
|
|
118
|
-
.returning(|_| Ok(vec![]));
|
|
119
|
-
|
|
120
|
-
mock.expect_disconnect()
|
|
121
|
-
.times(1)
|
|
122
|
-
.in_sequence(&mut seq)
|
|
123
|
-
.returning(|| Ok(()));
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Return Values
|
|
128
|
-
|
|
129
|
-
```rust
|
|
130
|
-
// Fixed value
|
|
131
|
-
mock.expect_count().returning(|| 42);
|
|
132
|
-
|
|
133
|
-
// Based on input
|
|
134
|
-
mock.expect_double().returning(|x| x * 2);
|
|
135
|
-
|
|
136
|
-
// Different values per call
|
|
137
|
-
mock.expect_next()
|
|
138
|
-
.times(3)
|
|
139
|
-
.returning(|| 1)
|
|
140
|
-
.returning(|| 2)
|
|
141
|
-
.returning(|| 3);
|
|
142
|
-
|
|
143
|
-
// Return owned values
|
|
144
|
-
mock.expect_get_name()
|
|
145
|
-
.returning(|| "Alice".to_string());
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Mocking External Traits
|
|
149
|
-
|
|
150
|
-
```rust
|
|
151
|
-
// For traits you don't own
|
|
152
|
-
#[cfg_attr(test, mockall::automock)]
|
|
153
|
-
trait HttpClient {
|
|
154
|
-
fn get(&self, url: &str) -> Result<Response, Error>;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// In production
|
|
158
|
-
struct RealHttpClient;
|
|
159
|
-
impl HttpClient for RealHttpClient {
|
|
160
|
-
fn get(&self, url: &str) -> Result<Response, Error> { /* ... */ }
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// In tests
|
|
164
|
-
#[cfg(test)]
|
|
165
|
-
fn mock_client() -> MockHttpClient {
|
|
166
|
-
let mut mock = MockHttpClient::new();
|
|
167
|
-
mock.expect_get()
|
|
168
|
-
.returning(|_| Ok(Response::new(200, "OK")));
|
|
169
|
-
mock
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Async Mocking
|
|
174
|
-
|
|
175
|
-
```rust
|
|
176
|
-
#[automock]
|
|
177
|
-
#[async_trait]
|
|
178
|
-
trait AsyncDatabase {
|
|
179
|
-
async fn fetch(&self, id: u64) -> Option<Data>;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
#[tokio::test]
|
|
183
|
-
async fn test_async() {
|
|
184
|
-
let mut mock = MockAsyncDatabase::new();
|
|
185
|
-
|
|
186
|
-
mock.expect_fetch()
|
|
187
|
-
.returning(|_| Some(Data::default()));
|
|
188
|
-
|
|
189
|
-
let result = mock.fetch(1).await;
|
|
190
|
-
assert!(result.is_some());
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## Design for Testability
|
|
195
|
-
|
|
196
|
-
```rust
|
|
197
|
-
// Accept trait, not concrete type
|
|
198
|
-
struct Service<D: Database> {
|
|
199
|
-
db: D,
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
impl<D: Database> Service<D> {
|
|
203
|
-
fn new(db: D) -> Self {
|
|
204
|
-
Self { db }
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Tests use mock
|
|
209
|
-
#[test]
|
|
210
|
-
fn test_service() {
|
|
211
|
-
let mock = MockDatabase::new();
|
|
212
|
-
let service = Service::new(mock);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Production uses real implementation
|
|
216
|
-
fn main() {
|
|
217
|
-
let db = PostgresDatabase::connect();
|
|
218
|
-
let service = Service::new(db);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## See Also
|
|
223
|
-
|
|
224
|
-
- [test-mock-traits](./test-mock-traits.md) - Mock trait design
|
|
225
|
-
- [test-proptest-properties](./test-proptest-properties.md) - Property testing
|
|
226
|
-
- [test-arrange-act-assert](./test-arrange-act-assert.md) - Test structure
|
package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# test-proptest-properties
|
|
2
|
-
|
|
3
|
-
> Use proptest for property-based testing
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Property-based testing generates random inputs to verify that properties hold across all possible values, not just hand-picked examples. Proptest finds edge cases you wouldn't think to test manually—empty strings, integer overflows, unicode edge cases.
|
|
8
|
-
|
|
9
|
-
## Setup
|
|
10
|
-
|
|
11
|
-
```toml
|
|
12
|
-
# Cargo.toml
|
|
13
|
-
[dev-dependencies]
|
|
14
|
-
proptest = "1.0"
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Basic Usage
|
|
18
|
-
|
|
19
|
-
```rust
|
|
20
|
-
use proptest::prelude::*;
|
|
21
|
-
|
|
22
|
-
proptest! {
|
|
23
|
-
#[test]
|
|
24
|
-
fn test_reverse_reverse_is_identity(s in ".*") {
|
|
25
|
-
let reversed: String = s.chars().rev().collect();
|
|
26
|
-
let double_reversed: String = reversed.chars().rev().collect();
|
|
27
|
-
assert_eq!(s, double_reversed);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
#[test]
|
|
31
|
-
fn test_sort_is_idempotent(mut v in prop::collection::vec(any::<i32>(), 0..100)) {
|
|
32
|
-
v.sort();
|
|
33
|
-
let sorted = v.clone();
|
|
34
|
-
v.sort();
|
|
35
|
-
assert_eq!(v, sorted);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Common Strategies
|
|
41
|
-
|
|
42
|
-
```rust
|
|
43
|
-
use proptest::prelude::*;
|
|
44
|
-
|
|
45
|
-
proptest! {
|
|
46
|
-
// Any type implementing Arbitrary
|
|
47
|
-
#[test]
|
|
48
|
-
fn test_i32(x in any::<i32>()) { }
|
|
49
|
-
|
|
50
|
-
// Regex-based string generation
|
|
51
|
-
#[test]
|
|
52
|
-
fn test_email(email in "[a-z]+@[a-z]+\\.[a-z]{2,3}") { }
|
|
53
|
-
|
|
54
|
-
// Ranges
|
|
55
|
-
#[test]
|
|
56
|
-
fn test_range(x in 0..100i32) { }
|
|
57
|
-
|
|
58
|
-
// Collections
|
|
59
|
-
#[test]
|
|
60
|
-
fn test_vec(v in prop::collection::vec(any::<i32>(), 0..10)) { }
|
|
61
|
-
|
|
62
|
-
// Optionals
|
|
63
|
-
#[test]
|
|
64
|
-
fn test_option(opt in prop::option::of(any::<i32>())) { }
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Custom Strategies
|
|
69
|
-
|
|
70
|
-
```rust
|
|
71
|
-
use proptest::prelude::*;
|
|
72
|
-
|
|
73
|
-
#[derive(Debug, Clone)]
|
|
74
|
-
struct User {
|
|
75
|
-
name: String,
|
|
76
|
-
age: u8,
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
fn user_strategy() -> impl Strategy<Value = User> {
|
|
80
|
-
("[a-zA-Z]{1,20}", 0..120u8)
|
|
81
|
-
.prop_map(|(name, age)| User { name, age })
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
proptest! {
|
|
85
|
-
#[test]
|
|
86
|
-
fn test_user(user in user_strategy()) {
|
|
87
|
-
assert!(user.age < 150);
|
|
88
|
-
assert!(!user.name.is_empty());
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Or derive Arbitrary
|
|
93
|
-
use proptest_derive::Arbitrary;
|
|
94
|
-
|
|
95
|
-
#[derive(Debug, Arbitrary)]
|
|
96
|
-
struct Point {
|
|
97
|
-
x: i32,
|
|
98
|
-
y: i32,
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Properties to Test
|
|
103
|
-
|
|
104
|
-
| Property | Example |
|
|
105
|
-
|----------|---------|
|
|
106
|
-
| Roundtrip | `decode(encode(x)) == x` |
|
|
107
|
-
| Idempotence | `f(f(x)) == f(x)` |
|
|
108
|
-
| Commutativity | `f(a, b) == f(b, a)` |
|
|
109
|
-
| Associativity | `f(f(a, b), c) == f(a, f(b, c))` |
|
|
110
|
-
| Identity | `f(x, identity) == x` |
|
|
111
|
-
| Invariants | `len(push(v, x)) == len(v) + 1` |
|
|
112
|
-
|
|
113
|
-
## Example: Parser Roundtrip
|
|
114
|
-
|
|
115
|
-
```rust
|
|
116
|
-
proptest! {
|
|
117
|
-
#[test]
|
|
118
|
-
fn parse_roundtrip(config in valid_config_strategy()) {
|
|
119
|
-
let serialized = config.to_string();
|
|
120
|
-
let parsed = Config::parse(&serialized).unwrap();
|
|
121
|
-
assert_eq!(config, parsed);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## Shrinking
|
|
127
|
-
|
|
128
|
-
Proptest automatically shrinks failing inputs to minimal cases:
|
|
129
|
-
|
|
130
|
-
```rust
|
|
131
|
-
// If this fails with vec![100, 50, 75, 25, 0]
|
|
132
|
-
// Proptest will shrink to vec![1, 0] (minimal failing case)
|
|
133
|
-
proptest! {
|
|
134
|
-
#[test]
|
|
135
|
-
fn test_sorted(v in prop::collection::vec(0..1000i32, 1..100)) {
|
|
136
|
-
let sorted = is_sorted(&v);
|
|
137
|
-
// This will fail and shrink
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Configuration
|
|
143
|
-
|
|
144
|
-
```rust
|
|
145
|
-
proptest! {
|
|
146
|
-
#![proptest_config(ProptestConfig {
|
|
147
|
-
cases: 1000, // More test cases
|
|
148
|
-
max_shrink_iters: 10000, // More shrinking
|
|
149
|
-
..ProptestConfig::default()
|
|
150
|
-
})]
|
|
151
|
-
|
|
152
|
-
#[test]
|
|
153
|
-
fn extensive_test(x in any::<i32>()) { }
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## See Also
|
|
158
|
-
|
|
159
|
-
- [test-criterion-bench](./test-criterion-bench.md) - Benchmarking
|
|
160
|
-
- [test-mockall-mocking](./test-mockall-mocking.md) - Mocking
|
|
161
|
-
- [test-arrange-act-assert](./test-arrange-act-assert.md) - Test structure
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# test-should-panic
|
|
2
|
-
|
|
3
|
-
> Use `#[should_panic]` to test that code panics as expected
|
|
4
|
-
|
|
5
|
-
## Why It Matters
|
|
6
|
-
|
|
7
|
-
Some code should panic on invalid inputs or invariant violations. `#[should_panic]` verifies the panic occurs, optionally checking the panic message. This ensures defensive panics work correctly and documents expected panic conditions.
|
|
8
|
-
|
|
9
|
-
## Bad
|
|
10
|
-
|
|
11
|
-
```rust
|
|
12
|
-
#[test]
|
|
13
|
-
fn test_panic() {
|
|
14
|
-
// Just calling panicking code makes test fail
|
|
15
|
-
divide(1, 0); // Test fails with panic
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Using catch_unwind is verbose
|
|
19
|
-
#[test]
|
|
20
|
-
fn test_panic_manual() {
|
|
21
|
-
let result = std::panic::catch_unwind(|| divide(1, 0));
|
|
22
|
-
assert!(result.is_err());
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Good
|
|
27
|
-
|
|
28
|
-
```rust
|
|
29
|
-
#[test]
|
|
30
|
-
#[should_panic]
|
|
31
|
-
fn divide_by_zero_panics() {
|
|
32
|
-
divide(1, 0); // Test passes when this panics
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// With expected message
|
|
36
|
-
#[test]
|
|
37
|
-
#[should_panic(expected = "division by zero")]
|
|
38
|
-
fn divide_by_zero_panics_with_message() {
|
|
39
|
-
divide(1, 0); // Panics with "division by zero"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Partial message match
|
|
43
|
-
#[test]
|
|
44
|
-
#[should_panic(expected = "index out of bounds")]
|
|
45
|
-
fn index_panic_contains_message() {
|
|
46
|
-
let v = vec![1, 2, 3];
|
|
47
|
-
let _ = v[100]; // Message contains "index out of bounds"
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Testing Invariants
|
|
52
|
-
|
|
53
|
-
```rust
|
|
54
|
-
struct NonEmpty<T>(Vec<T>);
|
|
55
|
-
|
|
56
|
-
impl<T> NonEmpty<T> {
|
|
57
|
-
fn new(items: Vec<T>) -> Self {
|
|
58
|
-
assert!(!items.is_empty(), "NonEmpty cannot be empty");
|
|
59
|
-
NonEmpty(items)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
#[test]
|
|
64
|
-
#[should_panic(expected = "NonEmpty cannot be empty")]
|
|
65
|
-
fn non_empty_rejects_empty_vec() {
|
|
66
|
-
NonEmpty::new(Vec::<i32>::new());
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
#[test]
|
|
70
|
-
fn non_empty_accepts_non_empty_vec() {
|
|
71
|
-
let ne = NonEmpty::new(vec![1, 2, 3]);
|
|
72
|
-
assert_eq!(ne.0.len(), 3);
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## With expect() Messages
|
|
77
|
-
|
|
78
|
-
```rust
|
|
79
|
-
fn get_config_value(key: &str) -> String {
|
|
80
|
-
CONFIG.get(key)
|
|
81
|
-
.expect(&format!("missing required config: {}", key))
|
|
82
|
-
.to_string()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
#[test]
|
|
86
|
-
#[should_panic(expected = "missing required config: DATABASE_URL")]
|
|
87
|
-
fn missing_config_panics_with_key() {
|
|
88
|
-
get_config_value("DATABASE_URL");
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## When NOT to Use should_panic
|
|
93
|
-
|
|
94
|
-
```rust
|
|
95
|
-
// ❌ For recoverable errors - use Result
|
|
96
|
-
#[test]
|
|
97
|
-
#[should_panic] // Wrong: this should return Err, not panic
|
|
98
|
-
fn invalid_input_panics() {
|
|
99
|
-
parse_config("invalid"); // Should return Err, not panic
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ✅ Return Result and test the error
|
|
103
|
-
#[test]
|
|
104
|
-
fn invalid_input_returns_error() {
|
|
105
|
-
let result = parse_config("invalid");
|
|
106
|
-
assert!(result.is_err());
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Combining with Result
|
|
111
|
-
|
|
112
|
-
```rust
|
|
113
|
-
#[test]
|
|
114
|
-
#[should_panic]
|
|
115
|
-
fn test_panics() -> Result<(), Error> {
|
|
116
|
-
// Can combine with Result for setup
|
|
117
|
-
let data = setup_test_data()?;
|
|
118
|
-
|
|
119
|
-
// This should panic
|
|
120
|
-
process_invalid(&data);
|
|
121
|
-
|
|
122
|
-
Ok(()) // Never reached
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## See Also
|
|
127
|
-
|
|
128
|
-
- [err-result-over-panic](./err-result-over-panic.md) - Panic vs Result
|
|
129
|
-
- [err-expect-bugs-only](./err-expect-bugs-only.md) - When to use expect
|
|
130
|
-
- [test-descriptive-names](./test-descriptive-names.md) - Test naming
|