ExUnit testing patterns, Mox for mocking, StreamData for property-based testing, and Phoenix test cases for Elixir applications.
From clarcnpx claudepluginhub marvinrichter/clarc --plugin clarcThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Use this skill when:
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
# 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
# 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
# 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)
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
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
# 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
elixir-patterns skill for GenServer, Supervisor, and Phoenix context patternsrules/elixir/testing.md for project-level testing standards