Conclujon is meant to help you with writing living documentation. It is inspired by Concordion but tries to fit the functional Clojure world better by doing some things differently. It is also in a very early stage (you might call it a proof of concept), so expect things to change and break often.
If you are familiar with Specification by Example, you might remember the part about automating validation without changing specification. While there is no lack of great libraries for automating BDD style examples, I found translating requirements into Concordion's HTML-based test format a lot more flexible.
Concordion is well integrated into the Java test ecosystem. That's a good thing. It does make implementing fixtures in Clojure awkward at best, thought. Conclujon on the other hand is conceived with Clojure in mind and tries not to make any further assumption about your application. Being able to rely on Enlive for the heavy lifting makes reinventing the wheel a lot less tedious, too.
To get started, add [conclujon "0.1.1"]
to your project's dependencies (preferably inside the :dev
profile).
Conclujon test cases consist of two parts which are joined together by the library by conventions. You express your test case in HTML:
<html>
<head>
<style type="text/css">
.failed { text-decoration: line-through; color: black; background-color: red; }
.passed { color: black; background-color: green; }
.actual { color: black; background-color: red; padding-left: 5px; }
</style>
</head>
<body>
<p>
A person named <em data-conclujon:assoc-input=":name">Stranger</em> will be
greeted with <em data-conclujon:assert-equals="greeting">Hello Stranger!</em>.<br/>
</p>
A person named <em data-conclujon:assoc-input=":name">Nils</em> will be
greeted with <em data-conclujon:assert-equals="greeting">Hello Niels!</em>.
</p>
</body>
</html>
Then you execute the test case against your application by writing fixtures in Clojure:
(ns spec.hello-world-test
(:require [conclujon.core :refer [conclujon-spec]]))
(defn greeting
"The fixture called by the data-conclujon:assoc-equals='greeting' element.
Usually, this will act as an adapter for your application code (e.g. calling
a greeting service, scraping a website, etc.)"
[{name :name}]
(str "Hello " name "!"))
;; This tells conclujon to look for and execute an HTML specification.
;; For fixtures in the spec.hello-world-test namespace, it expects the
;; specification to be found at spec/hello_world_test.html
(conclujon-spec basic-test)
As the conclujon-spec
macro generates a clojure.test
-compatible test case, you can simply run your test cases with by calling lein test
. The results will be a failed test and an HTML file target/conclujon/spec/hello_world_test.html
with the following content:
<html>
<head>
<style type="text/css">
.failed { text-decoration: line-through; color: black; background-color: red; }
.passed { color: black; background-color: green; }
.actual { color: black; background-color: red; padding-left: 5px; }
</style>
</head>
<body>
<p>
A person named <em data-conclujon:assoc-input=":name">Stranger</em> will be
greeted with <em class="passed" data-conclujon:assert-equals="greeting">Hello Stranger!</em>.<br/>
</p>
A person named <em data-conclujon:assoc-input=":name">Nils</em> will be
greeted with <em class="failed" data-conclujon:assert-equals="greeting">Hello Niels!</em><span class="actual">(actual: Hello Nils!)</span>.
</p>
</body>
</html>
When parsing an HTML specification, Conclujon will create batches and execute them seperately according to the following rules:
- Build a map from all
data-conclujon:assoc-input
attributes occuring before the nextdata-conclujon:assert-equals
attribute by association the value of theassoc-input
attribute with the content of the node it is attached to. - In turn, resolve the values of all following
data-conclujon:assert-equals
attributes as function in the current namespace and call them with the map generated in step 1. - Compare the result of the function call to the content of the name the
assert-equals
attribute is attached to. - If content and result match, add the
passed
class to the corresponding node. If content and result do not match, add thefailed
class to the corresponding name and insert another node with the actual result behind it. - Write out the modified HTML files and let the
clojure.test
test fail if any assertion failed.
Apparently, the current feature set of Conclujon is minimal. The next things on the roadmap are:
- Add unit tests and travis-ci configuration
- Tabular scenarios
- Support for destructuring in assert-equals (similar to the
concordion:execute="#var = methodCall"
directive)
Copyright © 2013 Nils Wloka
Distributed under the Eclipse Public License, the same as Clojure.