My last blog ‘Prop’ up your tests with test.check walked through an example of using Property Based Testing (PBT) on a very simple RESTful API.
However, this approach had limitations.
I could test the properties of each call on the API in isolation but what if I wanted to generate tests and assert about properties that spanned calls to the API.
I want to be able to generate random get
, post
and delete
methods for randomly generated resources and be able to assert on these randomly generated commands. For example, if my PBT’s generate a post
followed by a get
for the same resource I should find the resource but if the tests generate a post, delete, get
sequence for the same resource I should get a 404
(not found) on the get
.
This gives me a couple of problems.
- How to generate random streams of commands with appropriate arguments.
- How to assert on properties based on the expected state of the resources on the server.
Stateful.Check
Fortunately there is a Clojure library for that.
Stateful.check allows me to model the state of any system and to generate random streams of commands with their associated pre and post conditions.
Due to a change in the implementation of the rose tree in the latest version of test.check I needed to use ‘Michael Drogalis’ fork of stateful.check
Stateful.check uses specification maps to model state, define how to generate arguments, check pre and post conditions, etc.
Stateful.check specifications have two parts: an abstract model (state
) and a real execution. It took me some trial and error to work out that the abstract model stores the functions you define to operate on either the model or the real values as symbols and records the order of commands and only during the execution phase does it replay this stack of functions but this time actually evaluating them against the real values.
The model (state
) is defined as a hash map and you supply the initial value of the model in the :init-state
value of the overall specification.
The state
map is used to model the state of the actual implementation. In my case the state
map models the state of Customer resources on the server for my simple Customer RESTful API.
To use stateful.check
I need to include it in my project.clj
file:
(defproject
....
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.3.0"]
[cheshire "5.5.0"]
[org.clojure/test.check "0.9.0"]
[com.gfredericks/test.chuck "0.2.6"]
; add stateful-check in dev profile
[mdrogalis/stateful-check "0.3.2"]]}})