Packages

Makina is a DSL for writing PBT models for stateful systems.

Current section

Files

Jump to
makina priv docs command.md
Raw

priv/docs/command.md

Provides a set of macros to define the commands of a Makina model.
A command in a Makina model is an action that can be performed during test execution. Every
command is defined in terms of the callbacks:
- `pre` is a predicate over the model state that controls the generation of the command.
- `args` is a function over the model state that generates the arguments to the command.
- `valid_args` is a predicate over the generated arguments it must be used to check the
correctness of arguments generated by `args`. It is only used during shrinking.
- `call` is the function that should be call during test execution. This function is the real
implementation we want to test.
- `next` is a function that computes the list of updates that will be performed over the model
state after the command execution.
- `post` is a predicate that checks the correctness of the result after the command execution.
- `weight`
## Basic command definition
A basic command declaration is show below:
# 1) Create a module that will store the command
defmodule Model do
# 2) Use Makina.Command to configure the module and import the necessary macros
use Makina.Command
# 3) Write the command definition, where type declarations are optional but recommended.
defcommand f(arg1 :: integer()) :: :ok do
def args, do: %{arg1: nat()}
def call, do: :ok
end
Notice that in the previous code, there is only definitions of `args` and `call`. The only
callback which does not have a default implementation is `call` and should always be provided by
the user.
## Delegating `call` to other modules
Usually the functions to be tested are defined in other modules. `Makina.Command` provides a
mechanism to delegate `call` callbacks to the implementation in some other module. Imagine that we
want to test the function `f` in `Impl`:
defmodule Impl do
def f(), do: :ok
end
To delegate the execution of the command `f` to the function defined in `Impl` we just need to use
`implemented_by`:
defmodule Model do
use Makina.Command, implemented_by: Impl
defcommand f() :: :ok
end
## Extending commands
Sometimes when testing complex systems we need to refine the behavior of a command. Imagine that
we want to check that `f` always returns `:ok`. We can refine the callback as follows:
defmodule Refined do
use Makina.Command, extends: Model
defcommand f() :: :ok do
def post, result == :ok
end
end