Arnaud Bailly - @dr_c0d3
2024-03-15
quickcheck-dynamic
: Practicequickcheck-dynamic
: TheoryGoing full circle
Automata Testing → Test-Driven Development → Property-Based Testing → Model-Based Testing
quickcheck-dynamic
Real-life example drawn from personal tool that needs to read/write events in a database.
Define a GADT of “interesting” Action
s to run
Interpret actions against a Model
interpret :: (Monad m, Eq a, Show a) => Action a -> StateT Model m (Maybe a)
interpret (WriteEvent f) = do
modify $ \m@Model {events} -> m {events = events |> EventView {index = fromIntegral (Seq.length events + 1), event = f}}
pure $ Just ()
interpret (ReadEvents (Page pageNum size)) = do
es <- getEvents
...
Generate (meaningful) sequence of Action
s:
Run interpreter and SUT in parallel and check conformance:
canReadFlowsAndTracesWritten ::
(DB db, HasCallStack) =>
FilePath -> (forall x. db x -> IO x) -> Property
canReadFlowsAndTracesWritten dbFile nt =
forAllShrink (generateActions start) shrinkActions $
\(Actions actions) -> monadicIO $ do
res <- run $ nt $
initLogStorage >> evalStateT (validateActions actions) start
forM_ res monitorErrors
assert $ all isNothing res
quickcheck-dynamic
: PracticeOriginal research paper defines several key properties
Properties are (manually) expressed as Dynamic Logic formulas
quickcheck-dynamic
to produce
Executable Specification from Formal
SpecificationThe slogan is:
Agda Proofs are Quickcheck Tests
Testing Common Prefix property
Testing Common Prefix property
quickcheck-dynamic
: TheorySpecify possibles actions and initialState
Generate actions according to the current state
Define nextState
transition
Relate the specification to an actual (monadic) implementation
Specify postcondition
s that should hold after each
Action
Modality: [a]p
After a p
After action
a
,p
holds
AfterAny p
After any action
a
,p
holds
Constants: 𝟘, ∅
Stop
When execution
Stop
s, expression is true
Empty
When no execution is possible, expression is false
Alternatives: [a ∪ b]p, [a ∩ b]p
Alt Demonic f g
After
a
orb
p
must hold
Alt Angelic f g
p
must holdAfter
eithera
orb
Provide a more convenient syntax
Combinator to Gen
erate arbitrary values within
expression
From Thread Registry example
Can register an unbound thread under a new name
Cannot register an already registered thread under a new
name
Tie Dynamic Logic expression and Actions execution
into a Property
Tie StateModel and RunModel, interpreting
Action
s against the SUT
Sequence of actions that fail are shrank while respecting DL expression
anyActions_
tracesaction
and
anyAction
data according to model’s
shrinkAction
Quantifiable
values
generatedprecondition
filters invalid sequence
of actionsAction
s:
Positive
action is valid w.r.t to
the state and is expected to succeed when runNegative
action is invalid in
current state and is expected to failStateModel
works with symbolic values
IO
Dependencies make tests slow and
brittle
Model-Based Testing work hand-in-hand with Test-Driven Development
Development on quickcheck-dynamic is fairly active, with plans for:
A plan is useless, planning is everything
Gal. von Moltke
A model is useless, modeling is everything
quickcheck-dynamic