{"id":32164154,"url":"https://github.com/nshkrdotcom/supertester","last_synced_at":"2026-02-19T08:02:21.438Z","repository":{"id":305985615,"uuid":"1022313581","full_name":"nshkrdotcom/supertester","owner":"nshkrdotcom","description":"A battle-hardened testing toolkit for building robust and resilient Elixir \u0026 OTP applications.","archived":false,"fork":false,"pushed_at":"2026-01-10T02:18:40.000Z","size":383,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-02-17T01:54:17.064Z","etag":null,"topics":["beam","elixir","erlang-vm","functional-programming","nshkr-testing","otp","otp-testing","property-testing","quality-assurance","resilience","test-framework","test-utilities","testing","testing-toolkit","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nshkrdotcom.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-18T20:34:40.000Z","updated_at":"2026-02-16T13:09:39.000Z","dependencies_parsed_at":"2025-07-23T03:33:03.757Z","dependency_job_id":null,"html_url":"https://github.com/nshkrdotcom/supertester","commit_stats":null,"previous_names":["nshkrdotcom/supertester"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/nshkrdotcom/supertester","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Fsupertester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Fsupertester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Fsupertester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Fsupertester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nshkrdotcom","download_url":"https://codeload.github.com/nshkrdotcom/supertester/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Fsupertester/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29608152,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T06:47:36.664Z","status":"ssl_error","status_checked_at":"2026-02-19T06:45:47.551Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beam","elixir","erlang-vm","functional-programming","nshkr-testing","otp","otp-testing","property-testing","quality-assurance","resilience","test-framework","test-utilities","testing","testing-toolkit","testing-tools"],"created_at":"2025-10-21T14:42:50.469Z","updated_at":"2026-02-19T08:02:21.432Z","avatar_url":"https://github.com/nshkrdotcom.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Supertester\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/supertester-logo.svg\" alt=\"Supertester Logo\" width=\"200\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hex.pm/packages/supertester\"\u003e\u003cimg alt=\"Hex.pm\" src=\"https://img.shields.io/hexpm/v/supertester.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hexdocs.pm/supertester\"\u003e\u003cimg alt=\"Documentation\" src=\"https://img.shields.io/badge/docs-hexdocs-purple.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/nshkrdotcom/supertester/actions\"\u003e\u003cimg alt=\"Build Status\" src=\"https://img.shields.io/github/actions/workflow/status/nshkrdotcom/supertester/ci.yml\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/hexpm/l/supertester.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n**A battle-hardened OTP testing toolkit with chaos engineering, performance testing, and zero-sleep synchronization for building robust Elixir applications.**\n\n**Version 0.5.1** - Eliminates the private @doc warning in TestableGenServer shims.\n\n---\n\n## The Problem: Flaky OTP Tests\n\nAre you tired of...\n- **Flaky tests** that fail randomly due to race conditions?\n- **`GenServer` name clashes** when running tests with `async: true`?\n- Littering your test suite with `Process.sleep/1` and hoping for the best?\n- Struggling to test process crashes, restarts, and complex supervision trees?\n\nWriting tests for concurrent systems is hard. Traditional testing methods often lead to fragile, non-deterministic, and slow test suites.\n\n## The Solution: Supertester\n\n`Supertester` provides a comprehensive suite of tools to write clean, deterministic, and reliable tests for your OTP applications. It replaces fragile timing hacks with robust synchronization patterns and provides powerful helpers for simulating and asserting complex OTP behaviors.\n\nWith `Supertester`, you can build a test suite that is **fast**, **parallel**, and **trustworthy**.\n\n## Key Features\n\n- **Rock-Solid Test Isolation**: Run all your tests with `async: true` without fear of process name collisions or state leakage.\n- **Zero Process.sleep**: Complete elimination of timing-based synchronization. Use proper OTP patterns for deterministic testing.\n- **Powerful OTP Assertions**: Go beyond `assert`. Use `assert_process_restarted/2`, `assert_genserver_state/2`, and `assert_all_children_alive/1` for more expressive tests.\n- **Effortless Setup \u0026 Teardown**: Start isolated `GenServer`s and `Supervisor`s with a single line and trust `Supertester` to handle all the cleanup.\n- **Chaos Engineering**: Test system resilience with controlled fault injection, random process crashes, and resource exhaustion.\n- **Supervision Tree Testing**: Verify restart strategies, trace supervision events, and validate tree structures.\n- **Performance Testing**: Assert performance SLAs, detect memory leaks, and prevent regressions with built-in benchmarking.\n- **TestableGenServer**: Automatic injection of sync handlers for deterministic async operation testing.\n- **Concurrent Harness**: Describe multi-threaded scenarios once and let Supertester orchestrate calls, casts, chaos hooks, performance checks, and mailbox monitoring without extra glue.\n- **Telemetry Built-In**: Structured `:telemetry` events for scenario start/stop, mailbox sampling, chaos injections, and performance metrics so observability does not become an afterthought.\n\n## Installation\n\nAdd `supertester` as a dependency in your `mix.exs` file. It's only needed for the `:test` environment.\n\n```elixir\ndef deps do\n  [\n    {:supertester, \"~\u003e 0.5.1\", only: :test}\n  ]\nend\n```\n\nThen, run `mix deps.get` to install.\n\n## Quick Start: From Flaky to Robust\n\nSee how `Supertester` transforms a common, fragile test pattern into a robust, deterministic one.\n\n#### Before: The Flaky Way\n\n```elixir\n# test/my_app/counter_test.exs\ndefmodule MyApp.CounterTest do\n  use ExUnit.Case, async: false # \u003c-- Forced to run sequentially\n\n  test \"incrementing the counter\" do\n    # Manual setup, prone to name conflicts\n    {:ok, _pid} = start_supervised({Counter, name: Counter})\n\n    GenServer.cast(Counter, :increment)\n    Process.sleep(50) # \u003c-- Fragile, timing-dependent guess\n\n    state = GenServer.call(Counter, :state)\n    assert state.count == 1\n  end\nend\n```\n\n#### After: The Supertester Way\n\n```elixir\n# test/my_app/counter_test.exs\ndefmodule MyApp.CounterTest do\n  use Supertester.ExUnitFoundation, isolation: :full_isolation # \u003c-- Fully parallel!\n\n  # Import the tools you need\n  import Supertester.OTPHelpers\n  import Supertester.GenServerHelpers\n  import Supertester.Assertions\n\n  test \"incrementing the counter\" do\n    # Isolated setup with automatic cleanup, no name clashes\n    {:ok, counter_pid} = setup_isolated_genserver(Counter)\n\n    # Deterministic sync: ensures the cast is processed before continuing\n    :ok = cast_and_sync(counter_pid, :increment)\n\n    # Expressive, OTP-aware assertion for checking state\n    assert_genserver_state(counter_pid, fn state -\u003e state.count == 1 end)\n  end\nend\n```\n\n## Core Modules Overview\n\n`Supertester` is organized into several powerful modules, each targeting a specific area of OTP testing.\n\n- **`Supertester.ExUnitFoundation`**: Drop-in `ExUnit.Case` adapter that enables Supertester isolation with a single `use`, automatically configuring async support per isolation mode.\n\n- **`Supertester.UnifiedTestFoundation`**: The isolation runtime powering Supertester. Call it directly from custom harnesses or advanced setups where you don’t want to use the ExUnit adapter.\n\n- **`Supertester.TestableGenServer`**: A behavior that injects a `:__supertester_sync__` handler into your GenServers. This enables `cast_and_sync/2` to work deterministically: since GenServers process messages sequentially, a call that returns *after* a cast guarantees the cast was processed. No `Process.sleep/1` needed.\n\n- **`Supertester.OTPHelpers`**: Provides the essential tools for managing the lifecycle of isolated OTP processes. Functions like `setup_isolated_genserver/3` and `setup_isolated_supervisor/3` are your entry point for starting processes within the isolated test environment.\n\n- **`Supertester.GenServerHelpers`**: Contains specialized functions for interacting with `GenServer`s. The star of this module is `cast_and_sync/2`, which provides a robust way to test asynchronous `cast` operations deterministically. It also includes helpers for stress-testing and crash recovery scenarios.\n\n- **`Supertester.SupervisorHelpers`**: A dedicated toolkit for testing the backbone of OTP applications: supervisors. You can verify restart strategies, validate supervision tree structures, and trace supervision events to ensure your application is truly fault-tolerant.\n\n- **`Supertester.ConcurrentHarness`**: Compose complex concurrent scenarios declaratively. Provide thread scripts, invariants, optional chaos hooks, performance expectations, and mailbox monitoring, then receive a structured report plus telemetry events for each run.\n\n- **`Supertester.PropertyHelpers`**: Build `StreamData` generators that output ready-to-run concurrency scenarios. Feed them into `ConcurrentHarness` for property-based testing of GenServers and supervisors.\n\n- **`Supertester.MessageHarness`**: Trace messages delivered to any process while executing a function. Perfect for debugging mailbox growth or validating ordering without invasive instrumentation.\n\n- **`Supertester.Telemetry`**: Single entry point for emitting `:telemetry` events with the `[:supertester | ...]` prefix, covering scenario lifecycle, chaos hooks, mailbox samples, and performance measurements so you can plug into dashboards with minimal wiring.\n\n- **`Supertester.ChaosHelpers`**: Unleash controlled chaos to test your system's resilience. This module allows you to inject faults, kill random processes, and simulate resource exhaustion to ensure your system can withstand turbulent conditions.\n\n- **`Supertester.PerformanceHelpers`**: Integrate performance testing directly into your test suite. Assert that your code meets performance SLAs, detect memory leaks, and prevent performance regressions before they hit production.\n\n- **`Supertester.Assertions`**: A rich set of custom assertions that understand OTP primitives. Go beyond simple equality checks with assertions like `assert_genserver_state/2`, `assert_all_children_alive/1`, and `assert_no_process_leaks/1` for more expressive and meaningful tests.\n\n- **`Supertester.TelemetryHelpers`**: Async-safe telemetry testing with per-test event isolation. Attach handlers that only receive events tagged with the current test ID, assert telemetry events with pattern matching, and capture events during function execution.\n\n- **`Supertester.LoggerIsolation`**: Per-process Logger level isolation and log capture. Set Logger levels that only affect the current test process, capture log messages during execution, and restore original levels automatically on test completion.\n\n- **`Supertester.ETSIsolation`**: Isolated ETS table creation, mirroring, and table injection with automatic cleanup. Create test-scoped ETS tables, mirror production tables for safe testing, and inject isolated tables into processes under test.\n\n## Advanced Usage Examples\n\n### Chaos Engineering\n\nTest your system's resilience to failures:\n\n```elixir\ntest \"system survives random process crashes\" do\n  {:ok, supervisor} = setup_isolated_supervisor(MyApp.WorkerSupervisor)\n\n  # Kill 50% of workers over 3 seconds\n  report = chaos_kill_children(supervisor,\n    kill_rate: 0.5,\n    duration_ms: 3000,\n    kill_interval_ms: 200\n  )\n\n  # Verify system recovered\n  assert Process.alive?(supervisor)\n  assert report.supervisor_crashed == false\n  assert_all_children_alive(supervisor)\nend\n\n```\n\n```elixir\n\n# Mix chaos scenarios with concurrent harness runs\nsuite = [\n  %{type: :kill_children, kill_rate: 0.3, duration_ms: 500},\n  %{\n    type: :concurrent,\n    build: fn sup -\u003e\n      Supertester.ConcurrentHarness.simple_genserver_scenario(\n        MyWorker,\n        [{:cast, :do_work}, {:call, :get_state}],\n        4,\n        setup: fn -\u003e {:ok, sup, %{}} end,\n        cleanup: fn _, _ -\u003e :ok end\n      )\n    end\n  }\n]\n\nreport = run_chaos_suite(supervisor, suite)\n```\n\n### Performance Testing\n\nEnsure your code meets performance SLAs:\n\n```elixir\ntest \"API response time SLA\" do\n  {:ok, api_server} = setup_isolated_genserver(APIServer)\n\n  assert_performance(\n    fn -\u003e APIServer.handle_request(api_server, :get_user) end,\n    max_time_ms: 50,\n    max_memory_bytes: 500_000,\n    max_reductions: 100_000\n  )\nend\n\ntest \"no memory leak in message processing\" do\n  {:ok, worker} = setup_isolated_genserver(MessageWorker)\n\n  assert_no_memory_leak(10_000, fn -\u003e\n    MessageWorker.process(worker, generate_message())\n  end)\nend\n```\n\n### Supervision Tree Testing\n\nVerify supervision strategies work correctly:\n\n```elixir\ntest \"one_for_one restarts only failed child\" do\n  {:ok, supervisor} = setup_isolated_supervisor(MySupervisor)\n\n  result = test_restart_strategy(supervisor, :one_for_one,\n    {:kill_child, :worker_1}\n  )\n\n  assert result.restarted == [:worker_1]\n  assert :worker_2 in result.not_restarted\n  assert :worker_3 in result.not_restarted\nend\n\ntest \"supervision tree structure\" do\n  {:ok, root} = setup_isolated_supervisor(RootSupervisor)\n\n  assert_supervision_tree_structure(root, %{\n    supervisor: RootSupervisor,\n    strategy: :one_for_one,\n    children: [\n      {:cache, CacheServer},\n      {:worker_pool, WorkerPoolSupervisor}\n    ]\n  })\nend\n```\n\n### Concurrent Harness with Chaos and Telemetry\n\nOrchestrate multi-threaded scenarios with chaos injection and observability:\n\n```elixir\ntest \"counter stays non-negative under chaos\" do\n  scenario =\n    Supertester.ConcurrentHarness.simple_genserver_scenario(\n      CounterServer,\n      [{:cast, :increment}, {:cast, :decrement}],\n      5,\n      chaos: Supertester.ConcurrentHarness.chaos_kill_children(kill_rate: 0.2, duration_ms: 500),\n      performance_expectations: [max_time_ms: 100],\n      mailbox: [sampling_interval: 2],\n      metadata: %{test: \"counter chaos\"}\n    )\n\n  assert {:ok, report} = Supertester.ConcurrentHarness.run(scenario)\n  assert report.metrics.total_operations == length(report.events)\nend\n```\n\nAttach to telemetry events for scenario timing:\n\n```elixir\n:telemetry.attach(\n  \"supertester\",\n  [:supertester, :concurrent, :scenario, :stop],\n  fn _event, %{duration_ms: duration}, metadata, _ -\u003e\n    Logger.info(\"[supertester] scenario #{metadata.scenario_id} finished in #{duration}ms\")\n  end,\n  nil\n)\n```\n\n## Documentation\n\nFor a comprehensive guide to all features, modules, and functions, please see the full **[User Manual](guides/MANUAL.md)**.\n\nThe user manual includes:\n- In-depth explanations of every module.\n- Detailed function signatures, parameters, and return values.\n- Practical code examples and recipes for common testing scenarios.\n- Best practices for writing robust tests.\n\nAdditional documentation lives in `guides/`:\n\n- [Documentation Index](guides/DOCS_INDEX.md)\n- [Quick Start](guides/QUICK_START.md)\n- [API Guide](guides/API_GUIDE.md)\n\n## Examples\n\nIf you want a full Mix app that demonstrates every Supertester module, start here:\n\n- [Examples Index](examples/README.md)\n- [EchoLab Example App](examples/echo_lab/README.md)\n\n## What's New in 0.5.1\n\n- **TestableGenServer warning fix**: clears stale `@doc` attributes before defining the private sync helper.\n\nSee [CHANGELOG.md](CHANGELOG.md) for detailed changes.\n\n## Contributing\n\nContributions are welcome! If you'd like to help improve `Supertester`, please feel free to:\n1.  Fork the repository.\n2.  Create a new feature branch.\n3.  Add your feature or bug fix.\n4.  Ensure all new code is covered by tests.\n5.  Open a pull request.\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnshkrdotcom%2Fsupertester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnshkrdotcom%2Fsupertester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnshkrdotcom%2Fsupertester/lists"}