A Polly resilience policy is only as good as the faults it has been tested against. Without a systematic way to inject failures, you are left hoping that your retry or fallback logic is correct — only finding out when a real dependency fails in production. Simmy closes that gap by letting you inject precisely the right fault, at the right injection rate, in ordinary unit and integration tests. The key insight is that an injection rate ofDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Polly-Contrib/Simmy/llms.txt
Use this file to discover all available pages before exploring further.
1.0 makes a chaos policy fully deterministic: it always fires. That predictability is what makes Simmy so useful in automated tests — you get a repeatable, reproducible failure scenario without any mocking infrastructure.
Unit Testing Pattern
In unit tests you want complete control. Set the injection rate to1.0 so the fault fires on every execution, then wrap your resilience policy under test in a Policy.Wrap with the chaos policy innermost.
Build the chaos policy with 100 % injection rate
Always inject the fault so the test is deterministic. The
Enabled() call with no argument defaults to true.Wrap with the resilience policy under test
Place the chaos policy innermost so the outer resilience policy (retry, circuit breaker, fallback) reacts to the injected fault — exactly as it would react to a real dependency failure.
Testing Individual Resilience Scenarios
- Retry fires on injected exception
- Circuit breaker opens under sustained faults
- Fallback returns correct value
- Injected HTTP result (503)
Verify that your retry policy retries the correct number of times when Simmy injects the exception it handles.
Async Test Pattern
For async code paths use theAsync variants of all Simmy factory methods and pass a CancellationToken through.
Integration Testing with Low Injection Rates
In integration or load tests you typically want to simulate realistic conditions rather than forcing every call to fail. Use a low injection rate (0.01–0.05) and run enough iterations that you can statistically verify your system degrades gracefully.Low injection rates produce non-deterministic outcomes. Reserve them for integration or load tests where approximate ranges are acceptable assertions; use
1.0 whenever you need a hard guarantee in a unit test.Frequently Asked Questions
Why do I need Simmy when I can just mock the dependency?
Why do I need Simmy when I can just mock the dependency?
Mocking replaces the dependency entirely, so your resilience policy never executes. Simmy keeps the real execution path intact and injects the fault inside the policy boundary, which means the retry, circuit breaker, or fallback you have configured actually runs and must handle the fault. This tests the policy wiring, not just the happy-path logic.
Can I inject a fault only for a specific operation in a test?
Can I inject a fault only for a specific operation in a test?
Yes. Tag the
Execute call with a Context carrying an OperationKey, then use EnabledWhen to gate the fault on that key.How do I test InjectLatency without slowing down my test suite?
How do I test InjectLatency without slowing down my test suite?
Use a short latency value — even
TimeSpan.FromMilliseconds(100) — paired with a tight timeout policy. The goal is not to simulate the exact production latency value but to verify that your timeout policy fires when latency crosses its threshold.Do I need a separate chaos policy per fault type?
Do I need a separate chaos policy per fault type?
Yes — each
MonkeyPolicy injects one category of fault (exception, result, latency, or behaviour). If you want to test the interaction of multiple fault types in the same scenario, build separate policies and add them both to your PolicyWrap. Order them innermost-first so each outer resilience policy sees the combined effects.Can I use InjectBehaviour to simulate a partial outage?
Can I use InjectBehaviour to simulate a partial outage?
Yes.
InjectBehaviour lets you run any arbitrary Action before the real call is placed. You can use it to corrupt shared state, write a poison entry to a cache, or close a connection — anything that would degrade the call in a realistic way.