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-dynamicReal-life example drawn from personal tool that needs to read/write events in a database.
Define a GADT of “interesting” Actions 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 Actions:
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 resquickcheck-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 postconditions that should hold after each
Action
Modality: [a]p
After a p
After action
a,pholds
AfterAny p
After any action
a,pholds
Constants: 𝟘, ∅
Stop
When execution
Stops, expression is true
Empty
When no execution is possible, expression is false
Alternatives: [a ∪ b]p, [a ∩ b]p
Alt Demonic f g
After
aorbpmust hold
Alt Angelic f g
pmust holdAftereitheraorb
Provide a more convenient syntax
Combinator to Generate 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
Actions against the SUT
Sequence of actions that fail are shrank while respecting DL expression
anyActions_ tracesaction and
anyAction data according to model’s
shrinkActionQuantifiable values
generatedprecondition filters invalid sequence
of actions






Actions:
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 fail




StateModel 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
