elixir-testing
ExUnit testing patterns, Mox for mocking, StreamData for property-based testing, and Phoenix test cases for Elixir applications.
Best use case
elixir-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
ExUnit testing patterns, Mox for mocking, StreamData for property-based testing, and Phoenix test cases for Elixir applications.
Teams using elixir-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/elixir-testing/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How elixir-testing Compares
| Feature / Agent | elixir-testing | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
ExUnit testing patterns, Mox for mocking, StreamData for property-based testing, and Phoenix test cases for Elixir applications.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Elixir Testing
## When to Activate
Use this skill when:
- Writing ExUnit tests for Elixir or Phoenix applications
- Setting up Mox for mock-based testing
- Implementing property-based tests with StreamData
- Writing Phoenix controller or LiveView tests
- Setting up test factories with ExMachina
- Debugging flaky concurrent tests in Elixir
- Setting up code coverage with excoveralls
- Testing GenServer behavior and OTP processes
## ExUnit Basics
```elixir
defmodule MyApp.CalculatorTest do
use ExUnit.Case, async: true # Always async: true unless DB or shared state
describe "add/2" do
test "adds two positive numbers" do
assert Calculator.add(1, 2) == 3
end
test "handles negative numbers" do
assert Calculator.add(-1, 1) == 0
end
end
describe "divide/2" do
test "returns {:error, :division_by_zero} when divisor is 0" do
assert {:error, :division_by_zero} = Calculator.divide(10, 0)
end
end
end
```
## Mox for Compile-Safe Mocks
```elixir
# 1. Define behaviour
defmodule MyApp.Payments.Gateway do
@callback charge(amount :: integer(), token :: String.t()) ::
{:ok, map()} | {:error, String.t()}
end
# 2. Implementation
defmodule MyApp.Payments.StripeGateway do
@behaviour MyApp.Payments.Gateway
def charge(amount, token) do
Stripe.Charge.create(%{amount: amount, source: token, currency: "usd"})
end
end
# 3. Register mock in test/test_helper.exs
Mox.defmock(MyApp.MockGateway, for: MyApp.Payments.Gateway)
# 4. Configure application to use mock in test env
# config/test.exs
config :my_app, :payment_gateway, MyApp.MockGateway
# 5. Use in tests
defmodule MyApp.OrderServiceTest do
use ExUnit.Case, async: true
import Mox
setup :verify_on_exit!
test "processes payment on order creation" do
expect(MyApp.MockGateway, :charge, fn 1000, "tok_test" -> {:ok, %{id: "ch_123"}} end)
assert {:ok, order} = OrderService.create(%{amount: 1000, token: "tok_test"})
assert order.payment_id == "ch_123"
end
end
```
## DataCase for Database Tests
```elixir
# test/support/data_case.ex (generated by mix phx.new)
defmodule MyApp.DataCase do
use ExUnit.CaseTemplate
using do
quote do
alias MyApp.Repo
import Ecto
import Ecto.Changeset
import Ecto.Query
import MyApp.DataCase
end
end
setup tags do
MyApp.DataCase.setup_sandbox(tags)
:ok
end
def setup_sandbox(tags) do
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(MyApp.Repo, shared: not tags[:async])
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
end
end
# Usage
defmodule MyApp.AccountsTest do
use MyApp.DataCase
test "creates user with valid attrs" do
assert {:ok, user} = Accounts.create_user(%{email: "user@example.com"})
assert user.id
end
end
```
## ExMachina for Test Factories
```elixir
# test/support/factory.ex
defmodule MyApp.Factory do
use ExMachina.Ecto, repo: MyApp.Repo
def user_factory do
%MyApp.Accounts.User{
email: sequence(:email, &"user#{&1}@example.com"),
password_hash: Bcrypt.hash_pwd_salt("password123"),
role: :user
}
end
def admin_factory do
struct!(user_factory(), role: :admin)
end
def post_factory do
%MyApp.Blog.Post{
title: "Test Post #{System.unique_integer()}",
body: "Body content",
author: build(:user)
}
end
end
# Usage in tests
user = insert(:user)
admin = insert(:admin)
post = insert(:post, author: user)
```
## StreamData Property Testing
```elixir
defmodule MyApp.ValidatorTest do
use ExUnit.Case, async: true
use ExUnitProperties
property "email validator accepts all valid emails" do
check all local <- string(:alphanumeric, min_length: 1),
domain <- string(:alphanumeric, min_length: 1) do
email = "#{local}@#{domain}.com"
assert Validator.valid_email?(email)
end
end
property "encode then decode is identity" do
check all data <- binary() do
assert Base.decode64!(Base.encode64(data)) == data
end
end
end
```
## Testing GenServers
```elixir
defmodule MyApp.CacheTest do
use ExUnit.Case, async: true
setup do
{:ok, pid} = start_supervised(MyApp.Cache)
%{cache: pid}
end
test "stores and retrieves values" do
MyApp.Cache.put(:key, "value")
assert MyApp.Cache.get(:key) == "value"
end
test "returns nil for missing keys" do
assert MyApp.Cache.get(:missing) == nil
end
end
```
## Anti-Patterns
```elixir
# WRONG: Testing implementation, not behavior
test "calls Repo.insert" do
expect(MockRepo, :insert, fn _ -> {:ok, %User{}} end)
Users.create(%{email: "test@example.com"})
verify!(MockRepo)
end
# CORRECT: Test observable behavior
test "creates user in database" do
assert {:ok, user} = Users.create(%{email: "test@example.com"})
assert Repo.get(User, user.id)
end
# WRONG: Sleeping for async operations
test "sends email asynchronously" do
Users.register(%{email: "test@example.com"})
Process.sleep(100) # Flaky!
assert email_sent?()
end
# CORRECT: Use ExUnit's built-in async testing patterns
test "enqueues email job" do
assert {:ok, _} = Users.register(%{email: "test@example.com"})
assert_enqueued worker: WelcomeEmailWorker, args: %{email: "test@example.com"}
end
```
## Reference
- See `elixir-patterns` skill for GenServer, Supervisor, and Phoenix context patterns
- See `rules/elixir/testing.md` for project-level testing standardsRelated Skills
visual-testing
Visual Regression Testing: tool comparison (Chromatic/Percy/Playwright screenshots/BackstopJS), pixel-diff vs AI-based comparison, baseline management, flakiness strategies (masks, tolerances, waitForLoadState), CI integration with GitHub Actions, and Storybook integration.
typescript-testing
TypeScript testing patterns: Vitest for unit/integration, Playwright for E2E, MSW for API mocking, Testing Library for React components. Core TDD methodology for TypeScript/JavaScript projects.
swift-testing
Swift testing patterns: Swift Testing framework (Swift 6+), XCTest for UI tests, async/await test cases, actor testing, Combine testing, and XCUITest for UI automation. TDD for Swift/SwiftUI.
swift-protocol-di-testing
Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.
scala-testing
Scala testing with ScalaTest, MUnit, and ScalaCheck: FunSpec/FlatSpec test structure, property-based testing with forAll, mocking with MockitoSugar, Cats Effect testing with munit-cats-effect (runTest/IOSuite), ZIO Test, Testcontainers-Scala for database integration tests, and CI integration with sbt. Use when writing or reviewing Scala tests.
rust-testing
Rust testing patterns — unit tests with mockall, integration tests with sqlx transactions, HTTP handler testing (axum), benchmarks (criterion), property tests (proptest), fuzzing, and CI with cargo-nextest.
rust-testing-advanced
Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with
ruby-testing
RSpec testing patterns for Ruby and Rails — factories, mocks, request specs, feature specs, VCR, and SimpleCov coverage.
r-testing
R testing patterns: testthat 3e with expect_* assertions, snapshot testing, mocking with mockery and httptest2, covr code coverage, lintr static analysis, property-based testing with hedgehog, testing Shiny apps with shinytest2. Use when writing or reviewing R tests.
python-testing
Python testing strategies using pytest, TDD methodology, fixtures, mocking, and parametrization. Core testing fundamentals.
python-testing-advanced
Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.
php-testing
PHP testing patterns: PHPUnit 11 with mocks and data providers, Pest v3 with expectations and datasets, Laravel feature/HTTP tests with RefreshDatabase, Symfony WebTestCase, PHPStan static analysis, Infection mutation testing. Use when writing or reviewing PHP tests.