PropertyDamage.Flakiness (PropertyDamage v0.1.0)
View SourceDetect non-deterministic behavior in the system under test.
Flaky tests are tests that sometimes pass and sometimes fail with the same input. This is often caused by:
- Race conditions in the SUT
- External dependencies (time, network, etc.)
- Uninitialized state between runs
- Non-deterministic SUT behavior
Usage
# Check if a specific seed is flaky
result = PropertyDamage.check_determinism(
model: M,
adapter: A,
seed: 512902757,
runs: 10
)
case result do
{:ok, :deterministic} ->
IO.puts("Seed is deterministic")
{:ok, :flaky, stats} ->
IO.puts("Seed is FLAKY: passed #{stats.passes}/#{stats.runs} times")
{:error, reason} ->
IO.puts("Check failed: #{inspect(reason)}")
endBatch Checking
# Check multiple seeds at once
results = PropertyDamage.check_determinism_batch(
model: M,
adapter: A,
seeds: [123, 456, 789],
runs_per_seed: 5
)Understanding Results
The checker reports:
- Deterministic: Same result every run
- Flaky: Different results across runs
- Variance: What varies (pass/fail, failure type, shrunk size)
Summary
Functions
Check if a seed produces deterministic results.
Check multiple seeds for flakiness.
Run random seeds and identify flaky ones.
Format batch results for display.
Format flakiness check results for display.
Types
@type check_opts() :: [ runs: non_neg_integer(), adapter_config: map(), max_commands: non_neg_integer(), verbose: boolean() ]
@type flaky_stats() :: %{ runs: non_neg_integer(), passes: non_neg_integer(), failures: non_neg_integer(), failure_types: %{required(atom()) => non_neg_integer()}, shrunk_sizes: [non_neg_integer()], variance_type: :pass_fail | :failure_type | :shrunk_size | :multiple }
@type result() :: {:ok, :deterministic} | {:ok, :flaky, flaky_stats()} | {:error, term()}
Functions
@spec check(module(), module(), integer(), check_opts()) :: result()
Check if a seed produces deterministic results.
Runs the same seed multiple times and compares outcomes.
Options
:runs- Number of times to run (default: 5):adapter_config- Adapter configuration:max_commands- Maximum commands per run (default: 50):verbose- Print progress (default: false)
Returns
{:ok, :deterministic}- Same result every time{:ok, :flaky, stats}- Different results, with statistics{:error, reason}- Check failed
@spec check_batch(module(), module(), [integer()], keyword()) :: %{ required(integer()) => result() }
Check multiple seeds for flakiness.
Options
Same as check/4, plus:
:runs_per_seed- Runs per seed (default: 5):parallel- Run seeds in parallel (default: false)
Returns
Map from seed to result.
@spec discover_flaky(module(), module(), keyword()) :: [{integer(), flaky_stats()}]
Run random seeds and identify flaky ones.
This is useful for discovering non-determinism in your SUT without knowing specific problematic seeds.
Options
:num_seeds- Number of random seeds to test (default: 10):runs_per_seed- Runs per seed (default: 3)- Other options passed to
check/4
Returns
List of {seed, flaky_stats} for seeds that are flaky.
Format batch results for display.
Format flakiness check results for display.