Integrations¶
In plain English
ordeal connects to other testing tools so you can combine their strengths. The built-in Explorer handles most chaos testing. Integrations are for when you need something specialized: Atheris for deep coverage-guided fuzzing, the built-in OpenAPI engine for testing APIs under fault conditions. Think of them as power tools you reach for when the basics aren't enough.
When to use integrations¶
ordeal's core -- ChaosTest, buggify, the Explorer -- works standalone with no extra dependencies beyond Hypothesis. Integrations extend it with specialized engines for specific problem domains:
- Atheris: when you want coverage-guided fuzzing at the byte level, steering buggify() decisions based on code coverage feedback. Best for protocol parsing, serialization logic, and complex input processing where the input space is vast and you need systematic exploration.
- API chaos testing: when you have an OpenAPI spec and want to chaos-test your API layer. Built-in — no extra install needed. Best for web services, REST APIs, and microservices where you want to verify that fault conditions do not produce crashes or inconsistent responses.
If you are testing a stateful service or data pipeline, the built-in Explorer is usually the right tool. Reach for integrations when you need deeper coverage (Atheris) or schema-driven API testing (built-in engine).
Atheris (coverage-guided fuzzing)¶
What this unlocks
Atheris is Google's fuzzing engine. Where the Explorer tries random fault combinations, Atheris watches which lines of code each combination reaches and steers toward combinations that reach new code. It's slower but more systematic -- it will eventually find fault combinations that random exploration misses. Best for parsers, security-critical code, and anything where "we tried hard and found nothing" needs to mean something. The bridge lives in ordeal/integrations/atheris_engine.py.
Install¶
How it works¶
Atheris is Google's libFuzzer port for Python. It generates byte sequences and mutates them based on coverage feedback -- when a mutation reaches new code paths, the fuzzer remembers it and builds on it.
ordeal bridges Atheris to buggify(). Each buggify() call in your code consumes bytes from the fuzzer's input stream. The return value of buggify() (True or False) is determined by those bytes. Coverage feedback then steers the fuzzer toward byte sequences -- which correspond to specific fault combinations -- that reach new code paths.
The result: instead of random fault injection, Atheris systematically discovers which combinations of buggify() decisions lead to interesting behavior.
When to use Atheris¶
Use Atheris when you need the deepest possible exploration. It is slower than the Explorer (each run has overhead from coverage instrumentation), but more systematic. It will find fault combinations that random exploration misses.
Best for:
- Security-sensitive code: parsers, deserializers, protocol handlers where a crash is a vulnerability.
- Complex input processing: functions with many conditional branches that depend on input structure.
- Exhaustive validation: when you need confidence that no combination of buggify() decisions triggers a bug.
For general-purpose chaos testing of services, the Explorer is faster and easier to configure. Reach for Atheris when the Explorer has stopped finding new edges and you want to go deeper.
Limitations¶
- Requires the
ordeal[atheris]extra. Atheris itself can be tricky to install on some platforms (it needs Clang for native extension compilation). - Coverage instrumentation adds overhead. Expect 5-10x slower execution per test case compared to the Explorer.
- Atheris works at the function level. For stateful testing, use
fuzz_chaos_test(see below), but note that it drives rule selection with less sophistication than Hypothesis's stateful engine.
Fuzz a function with buggify¶
from ordeal.integrations.atheris_engine import fuzz
def my_function():
data = get_input()
if buggify():
data = corrupt(data)
process(data)
fuzz(my_function, max_time=60)
Each buggify() call consumes bytes from the fuzzer's stream. Coverage feedback steers toward fault combinations that reach new code.
Fuzz a ChaosTest class¶
from ordeal.integrations.atheris_engine import fuzz_chaos_test
fuzz_chaos_test(MyServiceChaos, max_time=300)
In this mode, Atheris drives rule selection, fault toggling, and step counts. The fuzzer's byte stream determines which rules execute and when faults activate, with coverage feedback guiding exploration.
Practical tips for Atheris¶
Start with a time limit, not an iteration count. Atheris explores breadth-first initially, then narrows as it finds interesting paths. 60 seconds is enough for most functions; security-critical code deserves 300s+.
Combine with buggify gates. The power of the Atheris integration is that each buggify() call consumes fuzzer bytes. More buggify gates = more decisions for the fuzzer to optimize. Place gates at every fault injection point:
def parse_message(data: bytes) -> Message:
if buggify():
data = data[:len(data)//2] # truncated input
if buggify():
data = data + b"\x00" * 10 # padded input
header = parse_header(data[:8])
if buggify():
header.version = 0 # force old code path
return decode_body(header, data[8:])
Check the crash corpus. Atheris saves inputs that caused crashes to a corpus directory. Replay them to verify fixes:
ls crash-* # Atheris crash files
python -c "import mymodule; mymodule.parse(open('crash-abc123', 'rb').read())"
API chaos testing¶
No extra install needed — the built-in engine uses only Hypothesis and the Python standard library.
How it works¶
ordeal reads your OpenAPI schema and generates HTTP requests that exercise every endpoint, including edge cases like boundary values, missing fields, and unusual content types. While sending requests, ordeal randomly toggles faults on your backend.
The combination tests a question that neither schema-driven generation nor fault injection answers alone: does your API behave correctly when the backend is experiencing faults? ordeal generates the traffic and creates the adverse conditions.
What it catches¶
- Endpoints that crash under fault conditions: a database timeout causes an unhandled exception instead of a 503.
- Inconsistent error responses: some faults produce proper error JSON, others produce stack traces or empty bodies.
- Data corruption through the API layer: a fault during write causes partial state that subsequent reads expose.
- Missing error handling: faults in dependencies that the API layer does not catch or translate.
Two patterns¶
Quick one-liner with chaos_api_test(): loads the schema, generates test cases for every endpoint, follows supported OpenAPI links into short stateful sequences, and randomly injects faults. Good for CI or quick validation.
from ordeal.integrations.openapi import chaos_api_test
from ordeal.faults import timing, io
chaos_api_test(
"http://localhost:8080/openapi.json",
faults=[
timing.slow("myapp.db.query", delay=2.0),
io.error_on_call("myapp.storage.save"),
],
)
# If your schema declares response links, stateful=True follows them automatically.
chaos_api_test("http://localhost:8080/openapi.json", faults=[...], stateful=True)
# With swarm mode — random fault subset per session, better coverage:
chaos_api_test(
"http://localhost:8080/openapi.json",
faults=[timing.slow("myapp.db.query"), io.error_on_call("myapp.storage.save")],
swarm=True,
)
@with_chaos decorator for pytest integration: gives you more control over assertions, fault selection, and test structure. Use this when you want to combine API testing with your existing test suite.
from ordeal.integrations.openapi import with_chaos
from ordeal.faults import timing
@with_chaos(faults=[timing.timeout("myapp.api.call")])
def test_api():
# Your API call logic here
response = call_my_api()
assert response.status_code in (200, 503)
The decorator randomly activates and deactivates faults before each API call, then resets them afterward to avoid cross-request interference.
Auto-fault discovery¶
ordeal can automatically generate faults from your source code using AST analysis:
from ordeal.integrations.openapi import chaos_api_test
# Explicit targets — generate mutation, semantic, and dependency faults:
chaos_api_test(
app=my_app,
mutation_targets=["myapp.handlers.create_order", "myapp.db.save"],
)
# Auto-discover — BFS app routes and their call graphs:
chaos_api_test(app=my_app, auto_discover=True)
Practical tips¶
Your API must be running (for URL mode). In-process testing with ASGI/WSGI apps needs no running server:
# ASGI (FastAPI, Starlette)
chaos_api_test(app=my_fastapi_app, faults=[...])
# WSGI (Flask, Django)
chaos_api_test(app=my_flask_app, wsgi=True, faults=[...])
Choose faults that match real failure modes. Don't just inject random faults — inject the ones your API actually encounters in production:
- Database slow/down:
timing.slow("myapp.db.query"),io.error_on_call("myapp.db.execute") - Upstream API failure:
timing.timeout("myapp.external.call"),io.return_empty("myapp.cache.get") - Storage issues:
io.disk_full(),io.permission_denied()
Check status codes, not just crashes. A 500 Internal Server Error is a bug. But so is a 200 with corrupted data.
Choosing between Explorer, Atheris, and API testing¶
| Explorer | Atheris | API testing | |
|---|---|---|---|
| What it tests | ChaosTest state machines | Functions or ChaosTests | API endpoints |
| Guidance | Edge-coverage hashing | Byte-level coverage (libFuzzer) | Schema-driven generation |
| Best for | Services, data pipelines, stateful systems | Parsers, security-critical code, serialization | Web services, REST APIs, microservices |
| Speed | Fast (no instrumentation overhead) | Slower (coverage instrumentation) | Depends on API response time |
| Depth | Good (energy scheduling, swarm mode) | Deepest (systematic byte mutation) | Broad (every endpoint, every parameter) |
| Setup | ordeal.toml + ChaosTest class | pip install ordeal[atheris] |
No extra install + OpenAPI schema |
| Output | Traces, JSON report, property results | Crash corpus (libFuzzer style) | ChaosAPIResult with fault stats |
Rules of thumb:
- Start with the Explorer. It works with any ChaosTest, requires no extra dependencies, and finds most bugs.
- Add Atheris when you need deeper coverage on specific functions, especially parsers or security-sensitive code.
- Add API testing when you have an API with a schema and want to verify fault tolerance at the HTTP layer.
- All three can run in CI. The Explorer and API testing produce structured reports; Atheris produces a crash corpus.