Fault injection deliberately triggers the failures a system is meant to tolerate, to check that the code written to handle them actually does. The injected faults include a failed allocation, a dropped packet, a corrupted record, a crashed dependency.
The technique spans levels. Chaos engineering injects faults at the system level on a production-like system, and deterministic simulation testing injects them inside a deterministic simulator. In-process injection forces one chosen internal operation to fail and exercises the error path that follows. It needs neither an orchestrator nor a simulator, which makes it the cheapest of the three to aim at one specific error path.
What it catches¶
- Untested error-handling paths. The catch block never entered in testing, the retry that deadlocks, the cleanup that leaks a resource when the operation it follows fails partway.
- Resource-exhaustion handling. Whether the code degrades or
corrupts under out-of-memory, disk-full or descriptor exhaustion —
a failed
malloc, a write erroring mid-stream. - Partial-failure handling. An RPC or syscall that fails after a side effect, leaving state half-updated; the bug is in what happens next.
- Transient hardware-fault tolerance. For safety-critical systems, software-implemented fault injection (SWIFI) corrupts registers and memory to test survival of bit-flips a normal test never produces.
What it does not catch: anything on the happy path, and faults outside the injected set, which the fault model bounds.
Tools¶
- libfiu — intercepts libc calls to fail them on demand (C/C++).
- failpoints — compile-time injection points toggled at runtime:
the Rust
failcrate, FreeBSD'sfail(9). - Byteman (Java) — rules that inject exceptions or delays at named code points.
- Toxiproxy — injects network faults (latency, drops) at a proxy, sitting between the in-process and system levels.
When to use, when not¶
Use it where error-handling or fault-tolerance code is critical and hard to trigger naturally — storage engines, allocators, network clients, retry and failover logic, safety-critical software.
Don't treat it as a substitute for happy-path testing, and don't trust a fault model that does not match real failure modes: injecting faults that cannot happen wastes effort, and missing the ones that can gives false confidence.
Evidence¶
The case is definitional rather than measured: error-handling code that never runs under normal load is untested code, and injecting the fault is the way to run it.
Further reading¶
- The technique and its hardware-vs-software taxonomy are surveyed by Hsueh, Tsai, and Iyer (Hsueh et al. 1997)1.
Related¶
Perturbation testing: what you perturb, and where
All three deliberately subject a system to bad conditions, differing in what they perturb and where. Fuzzing generates inputs (random, coverage-guided, or grammar-aware) and watches for crashes, undefined behavior, or sanitizer trips. Fault injection perturbs the environment in a test harness (a failed allocation, a dropped packet, a crashed dependency) to check the error-handling and fault-tolerance paths. Chaos engineering injects the same kind of environment faults into a running production system, verifying fallbacks under real conditions. Fuzzing probes input handling; fault injection and chaos probe failure resilience, in a harness and in production respectively.
Classification¶
- Quality dimensions: Reliability: safety.
- Area: Error-handling and fault-tolerance code that normal runs rarely exercise — storage and allocator error paths, RPC partial failures, retry and cleanup logic; safety-critical tolerance to transient hardware faults.
- Guarantee: Empirical — the faults that were injected were handled; faults outside the injected set say nothing.
Referenced by¶
- Snapshot and approval testing · Methods
- Verifying memory safety · Methods
- Verifying safety-critical systems · Methods
References¶
-
Hsueh, Mei-Chen, Timothy K. Tsai, and Ravishankar K. Iyer. 1997. "Fault Injection Techniques and Tools." Computer 30 (4): 75–82. https://doi.org/10.1109/2.585157. ↩