nw-pbt-erlang-elixir

Erlang/Elixir property-based testing with PropEr, PropCheck, and StreamData frameworks

322 stars

Best use case

nw-pbt-erlang-elixir is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Erlang/Elixir property-based testing with PropEr, PropCheck, and StreamData frameworks

Teams using nw-pbt-erlang-elixir 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

$curl -o ~/.claude/skills/nw-pbt-erlang-elixir/SKILL.md --create-dirs "https://raw.githubusercontent.com/nWave-ai/nWave/main/nWave/skills/nw-pbt-erlang-elixir/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/nw-pbt-erlang-elixir/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How nw-pbt-erlang-elixir Compares

Feature / Agentnw-pbt-erlang-elixirStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Erlang/Elixir property-based testing with PropEr, PropCheck, and StreamData frameworks

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

# PBT Erlang/Elixir -- PropEr, PropCheck, StreamData

## Framework Selection

| Framework | Language | Stateful | Parallel | Choose When |
|-----------|----------|----------|----------|-------------|
| PropEr | Erlang | Yes | Yes (linearizability) | Erlang projects needing full stateful/parallel testing |
| PropCheck | Elixir | Yes (via PropEr) | Yes (via PropEr) | Elixir projects needing stateful/parallel testing |
| StreamData | Elixir | No | No | Elixir projects needing only stateless PBT |

PropCheck wraps PropEr with Elixir syntax. StreamData is pure Elixir but lacks stateful testing.

## Quick Start

```erlang
%% PropEr (Erlang)
-include_lib("proper/include/proper.hrl").

prop_sort_preserves_length() ->
    ?FORALL(List, list(integer()),
        length(lists:sort(List)) =:= length(List)).

%% Run: proper:quickcheck(my_module:prop_sort_preserves_length()).
```

### Syntax Differences

| Concept | PropEr (Erlang) | PropCheck (Elixir) | StreamData (Elixir) |
|---------|-----------------|--------------------|--------------------|
| Import | `-include_lib("proper/include/proper.hrl").` | `use PropCheck` | `use ExUnitProperties` |
| Property | `?FORALL(X, gen(), body)` | `forall x <- gen() do body end` | `check all x <- gen() do assert body end` |
| Integer | `integer()` | `integer()` | `integer()` |
| List | `list(integer())` | `list(integer())` | `list_of(integer())` |
| Run | `proper:quickcheck(prop())` | `mix test` | `mix test` |

## Generator Cheat Sheet (PropEr)

### Primitives
```erlang
integer()                  % any integer
integer(0, 100)            % bounded
float()
binary()                   % binary data
boolean()
atom()
```

### Collections
```erlang
list(integer())            % list of integers
non_empty(list(integer())) % non-empty list
vector(5, integer())       % fixed-length list
{integer(), binary()}      % tuple (direct syntax)
```

### Combinators
```erlang
oneof([integer(), binary()])      % union
elements([a, b, c])               % pick from list
frequency([{80, integer()}, {20, atom()}])  % weighted

%% ?LET (map/transform)
even() ->
    ?LET(N, integer(), N * 2).

%% ?SUCHTHAT (filter -- use sparingly)
non_empty_list() ->
    ?SUCHTHAT(L, list(integer()), L =/= []).

%% Nested ?LET (dependent generation)
list_and_element() ->
    ?LET(List, non_empty(list(integer())),
        ?LET(Elem, elements(List),
            {List, Elem})).
```

### Recursive
```erlang
tree(Type) ->
    ?SIZED(Size, tree(Size, Type)).
tree(0, Type) -> {leaf, Type};
tree(Size, Type) ->
    frequency([
        {1, {leaf, Type}},
        {5, ?LAZY({node, Type, tree(Size div 2, Type), tree(Size div 2, Type)})}
    ]).
```

### Shrinking
```erlang
year() ->
    ?SHRINK(integer(0, 9999), [integer(1970, 2000)]).

date() ->
    ?LETSHRINK([Y, M, D],
               [integer(1, 9999), integer(1, 12), integer(1, 31)],
               {Y, M, D}).
```

## Stateful Testing (proper_statem)

```erlang
-behaviour(proper_statem).

initial_state() -> #{items => #{}}.

command(#{items := Items}) ->
    oneof([
        {call, ?MODULE, put, [key(), value()]},
        {call, ?MODULE, get, [elements(maps:keys(Items))]}
            || maps:size(Items) > 0
    ]).

precondition(#{items := Items}, {call, _, get, [Key]}) ->
    maps:is_key(Key, Items);
precondition(_, _) -> true.

postcondition(#{items := Items}, {call, _, get, [Key]}, Result) ->
    Result =:= maps:get(Key, Items);
postcondition(_, _, _) -> true.

next_state(State = #{items := Items}, _Var, {call, _, put, [Key, Val]}) ->
    State#{items := Items#{Key => Val}};
next_state(State, _, _) -> State.

prop_store() ->
    ?FORALL(Cmds, commands(?MODULE),
        begin
            {History, State, Result} = run_commands(?MODULE, Cmds),
            cleanup(),
            Result =:= ok
        end).
```

StreamData: No stateful testing. PropCheck: Same proper_statem callbacks with Elixir syntax.

### Parallel Testing (Linearizability)

```erlang
prop_store_parallel() ->
    ?FORALL(Cmds, parallel_commands(?MODULE),
        begin
            {Sequential, Parallel, Result} = run_parallel_commands(?MODULE, Cmds),
            cleanup(),
            Result =:= ok
        end).
```

Swap `commands` for `parallel_commands`, `run_commands` for `run_parallel_commands`. Framework generates sequential prefix + parallel branches and checks all linearizations.

## Test Runner Integration

```erlang
%% PropEr: {deps, [{proper, "1.4.0"}]}. Run: rebar3 proper
%% PropCheck: {:propcheck, "~> 1.4", only: :test}. Run: mix test
%% StreamData: {:stream_data, "~> 1.0", only: :test}. Run: mix test
```

## Unique Features

- **proper_fsm**: Finite state machine testing module (distinct from generic state machines)
- **PULSE integration**: Controlled scheduling for parallel test race detection
- **Symbolic references**: First-class support with two-phase (abstract + concrete) execution
- **collect/aggregate**: Built-in distribution analysis for generator quality verification
- **Linearizability checking**: One of the most mature implementations in any PBT framework

Related Skills

nw-ux-web-patterns

322
from nWave-ai/nWave

Web UI design patterns for product owners. Load when designing web application interfaces, writing web-specific acceptance criteria, or evaluating responsive designs.

nw-ux-tui-patterns

322
from nWave-ai/nWave

Terminal UI and CLI design patterns for product owners. Load when designing command-line tools, interactive terminal applications, or writing CLI-specific acceptance criteria.

nw-ux-principles

322
from nWave-ai/nWave

Core UX principles for product owners. Load when evaluating interface designs, writing acceptance criteria with UX requirements, or reviewing wireframes and mockups.

nw-ux-emotional-design

322
from nWave-ai/nWave

Emotional design and delight patterns for product owners. Load when designing onboarding flows, empty states, first-run experiences, or evaluating the emotional quality of an interface.

nw-ux-desktop-patterns

322
from nWave-ai/nWave

Desktop application UI patterns for product owners. Load when designing native or cross-platform desktop applications, writing desktop-specific acceptance criteria, or evaluating panel layouts and keyboard workflows.

nw-user-story-mapping

322
from nWave-ai/nWave

User story mapping for backlog management and outcome-based prioritization. Load during Phase 2.5 (User Story Mapping) to produce story-map.md and prioritization.md.

nw-tr-review-criteria

322
from nWave-ai/nWave

Review dimensions and scoring for root cause analysis quality assessment

nw-tlaplus-verification

322
from nWave-ai/nWave

TLA+ formal verification for design correctness and PBT pipeline integration

nw-test-refactoring-catalog

322
from nWave-ai/nWave

Detailed refactoring mechanics with step-by-step procedures, and test code smell catalog with detection patterns and before/after examples

nw-test-organization-conventions

322
from nWave-ai/nWave

Test directory structure patterns by architecture style, language conventions, naming rules, and fixture placement. Decision tree for selecting test organization strategy.

nw-test-design-mandates

322
from nWave-ai/nWave

Four design mandates for acceptance tests - hexagonal boundary enforcement, business language abstraction, user journey completeness, walking skeleton strategy, and pure function extraction

nw-tdd-review-enforcement

322
from nWave-ai/nWave

Test design mandate enforcement, test budget validation, 5-phase TDD validation, and external validity checks for the software crafter reviewer