Using logic programming (Clojure's core.logic) for test data manipulation and generation
Latest commit ca4ea2d Nov 23, 2012 @marick Upgrade to Lein 2

The goal of this project is to make it as easy as possible to construct test data, including hierarchical data with constraints on how datum X can be used with datum Y.

User documentation

Easy lookup of named tabular data

So, for example, suppose I have animals and medical procedures:

(data [animal :by :name :with-selectors]
      {:name "betty" :species :bovine :legs 4}
      {:name "hank" :species :equine :legs 4}
      {:name "jake" :species :equine :legs 3} ; poor jake
      {:name "dawn" :species :human :legs 2})

(data [procedure :by :name :with-selectors]
      {:name "hoof trim" :species-rule :equine-only :days-delay 0}
      {:name "superovulation" :species-rule :bovine-only :days-delay 90}
      {:name "physical exam" :species-rule :all :days-delay 0})

From this, we can auto-construct selectors that let us easily ask for the names of data that satisfy constraints.

  (animal?> :legs 4) ;; ["betty" "hank"]
  (one-procedure?> :days-delay 0) ;; "hoof trim"

Once you have names, you can use the tables to populate a database or whatever.

Groups of names that satisfy constraints.

Let's say the permitted?? relation/goal succeeds when a particular procedure is permitted on a particular animal. So, for example:

(permitted?? "hoof trim" "hank") ;; succeed
(permitted?? "hoof trim" "dawn") ;; fail

Now we can ask for pairs that are permitted:

(permitted?>) ;; [["hoof trim", "hank"], ["superovulation" "betty"]], etc.
(one-permitted?> :procedure "hoof trim") ;; ["hoof trim" "hank"], etc.

Hierarchical data

The app from which these examples are drawn is largely about reservations, which contain one or more (permitted) procedure/animal pairs. I want to show a "picture" of the "shape" of reservation I want, and then let an engine fill in the blanks.

For example, the most common reservation used in the tests is one group with one procedure/animal pair. In many many cases, I don't actually care which procedure and which animal I use. I want to describe it like this:

(reservation?> [- -] [- -])

... and get back a data structure something like this:

([["hank" "hoof trim"] ["betty" "superovulation"]] ...)

Then a database-population routine (that would look extremely like the one that already exists to add a reservation from json data) would take over.

Sometimes you'd want constraints. Some of those can be expressed in abbreviated form. Suppose I want two pairs, the first containing Hank the Horse and some appropriate procedure, the second containing a cow. That would look like this:

(reservation?>  ["hank" -] [{:species :bovine} -])

I have a prototype of this working. The big question is whether it's a plausible stunted framework: one that's not so universal that it's too hard to learn and too fiddly to work with, but that doesn't require so much customization that it's not worthwhile?

Another question: Logic programming is esoteric. The framework aims to write most of a core.logic query for you, but you need to provide some help. Can you use this framework without having to be an expert in core.logic?

And: is Clojure a large enough language community? one that builds the sort of apps that need sophisticated test data generation?


Copyright © 2012 Brian Marick

Distributed under the Eclipse Public License, the same as Clojure.