Clojure comes with support for unit testing and it possible to use this framework to unit test ontologies, both with and without reasoning. This allows automated checking both of expected inheritance patterns, and that inferencing is working as expected.
In this document, we use clojure.test
which is one of a number of test
frameworks, because it is already available in Clojure.
Tests in Clojure are rather like Java -- they are generally stored in a
directory structure which mirrors the src
tree. In clojure tests use a file
(and namespace) with -test
appended. A simple test structure looks like so:
(ns my.ontology-test
(:use [clojure.test])
(deftest test-name
(is
true)
(is
(not false)))
The test-name
is used as a label in any error messages; the is
form
asserts that something is true, that is returns true
(for Clojure's
definition of true
-- this is not the same as either Java or many Common
Lisp!). In general, the is
forms should be highly related tests; otherwise,
multiple deftest
forms are more appropriate.
clojure.test
provides support for "fixtures" -- functions that define the
environment within which this tests run. This turns out to be very useful for
tests in tawny
; generally, these are used to set the reasoner appropriately,
to set the reasoner progress monitor, and set the ontology to be tested to as
the current ontology for the test namespace. The following code from the pizza
ontology achieves this.
(defn ontology-reasoner-fixture [tests]
;; this should kill the reasoner factory and all reasoners which is the
;; safest, but slowest way to start.
(r/reasoner-factory :hermit)
;; inject the pizzaontology into the current namespace, which saves the
;; hassle of using with ontology every where. set this up each time in case
;; pizzaontology has been re-evaled
(o/ontology-to-namespace p/pizzaontology)
(binding [r/*reasoner-progress-monitor*
(atom r/reasoner-progress-monitor-silent)]
(tests)))
(use-fixtures :once ontology-reasoner-fixture)
The art of testing ontologies is not well-developed at the current time, so the main use for testing is checking for coherence, and the specific consequences of reasoning. Both of these are straight-forward.
(deftest consistency
(is (coherent?))
(is (consistent?)))
This test can be useful, including for ontologies written externally to OWL and read into tawny; tawny can be used to define unit tests against any OWL ontology whether written in tawny or not.
A second significant usage for testing is to determine whether specific
interferences has taken place. The tawny.reasoner
namespace provides a
number of tests for reasoned superclasses which allows testing for this. This
example from the pizza ontology demonstrates this usage.
(deftest CheesyShort
(is (r/isuperclass? p/FourCheesePizza p/CheesyPizza))
(is
(not (r/isuperclass? p/MargheritaPizza p/FourCheesePizza))))
A second use is to check for inferred disjointness; this is not done directly,
but by introducing a subclass of the two expected classes, and checking for
incoherence. A class introduced in this way is described as a "probe class"
tawny
provides explicit support for these classes, taking care of removing
them again afterwards. This example, for instance, tests whether our
definition of vegetarian pizza is behaving as expected.
(deftest VegetarianPizza
(is
(r/isuperclass? p/MargheritaPizza p/VegetarianPizza))
(is
(not
(o/with-probe-entities
[c (o/owlclass "probe"
:super p/VegetarianPizza p/CajunPizza)]
(r/coherent?)))))
The expected inference is checked in the first is
clause, while the second
is
clause checks that the ontology is now incoherent.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close