Liking cljdoc? Tell your friends :D

Test scaffolding

Let's say we want to write a test for the Zookeeper configuration store. We'll begin by creating a new Leiningen project:

$ lein new jepsen.zookeeper
Generating a project called jepsen.zookeeper based on the 'default' template.
The default template is intended for library projects, not applications.
To see other templates (app, plugin, etc), try `lein help new`.
$ cd jepsen.zookeeper

We'll need a few Clojure libraries for this test. Open project.clj, which specifies the project's dependencies and other metadata. In addition to depending on the Clojure language itself, we'll pull in the Jepsen library (at version 0.0.9), and Avout: a library for working with Zookeeper.

(defproject jepsen.zookeeper "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.7.0"]
                 [jepsen "0.0.9"]
                 [avout "0.5.4"]])

New lein projects include a trivial failing test, which we can run with lein test:

$ lein test

lein test jepsen.zookeeper-test

lein test :only jepsen.zookeeper-test/a-test

FAIL in (a-test) (zookeeper_test.clj:7)
FIXME, I fail.
expected: (= 0 1)
  actual: (not (= 0 1))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Tests failed.

We'll use lein test throughout this guide to re-run our Jepsen test. Let's start with a simple test that does nothing. In src/jepsen/zookeeper.clj, we'll require the jepsen.tests namespace and call it tests for short. Then we'll write a function zk-test which takes a zookeeper version string as an argument, ignores it, and returns a Jepsen test that does nothing. We'll use this noop-test as a scaffold, gradually hooking in more pieces as we build them.

(ns jepsen.zookeeper
  (:require [jepsen.tests :as tests]))

(defn zk-test
  [version]
  tests/noop-test)

tests/noop-test refers to the noop-test var in the jepsen.tests namespace--which we aliased to tests in the :require section of our namespace declaration.

Next, we'll replace the example test that lein generated (test/jepsen/zookeeper_test.clj) with one that calls the zk-test function, runs the test that function returns, looks at the results, and ensures that the :valid? key is true.

(ns jepsen.zookeeper-test
  (:require [clojure.test :refer :all]
            [jepsen.core :as jepsen]
            [jepsen.zookeeper :as zk]))

(deftest zk-test
  (is (:valid? (:results (jepsen/run! (zk/zk-test "3.4.5+dfsg-2"))))))

We can run this test to confirm that everything's hooked up correctly:

$ lein test
WARNING: run! already refers to: #'clojure.core/run! in namespace: jepsen.core, being replaced by: #'jepsen.core/run!
WARNING: run! already refers to: #'clojure.core/run! in namespace: jepsen.tests, being replaced by: #'jepsen.core/run!

lein test jepsen.zookeeper-test
INFO  jepsen.core - nemesis done
INFO  jepsen.core - Worker 0 starting
INFO  jepsen.core - Worker 2 starting
INFO  jepsen.core - Worker 0 done
INFO  jepsen.core - Worker 2 done
INFO  jepsen.core - Worker 1 starting
INFO  jepsen.core - Worker 4 starting
INFO  jepsen.core - Worker 3 starting
INFO  jepsen.core - Worker 1 done
INFO  jepsen.core - Worker 4 done
INFO  jepsen.core - Worker 3 done
INFO  jepsen.core - Waiting for nemesis to complete
INFO  jepsen.core - nemesis done.
INFO  jepsen.core - Tearing down nemesis
INFO  jepsen.core - Nemesis torn down
INFO  jepsen.core - Run complete, writing
INFO  jepsen.store - Wrote /home/aphyr/jepsen/jepsen.zookeeper/store/noop/20151231T155934.000-0800/history.txt
INFO  jepsen.store - Wrote /home/aphyr/jepsen/jepsen.zookeeper/store/noop/20151231T155934.000-0800/results.edn
INFO  jepsen.core - Analyzing
INFO  jepsen.core - Analysis complete
INFO  jepsen.store - Wrote /home/aphyr/jepsen/jepsen.zookeeper/store/noop/20151231T155934.000-0800/history.txt
INFO  jepsen.store - Wrote /home/aphyr/jepsen/jepsen.zookeeper/store/noop/20151231T155934.000-0800/results.edn
INFO  jepsen.core - Everything looks good! ヽ(‘ー`)ノ

{:valid? true, :configs ({:model {}, :pending []}), :final-paths ()}


Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

We can see Jepsen start a series of workers--each one responsible for executing operations against the database--and a nemesis, which causes failures. We haven't given them anything to do, so they shut down immediately. Jepsen spits out a text file describing the clients' and nemesis' actions to history.txt, and a full dump of the test results to results.edn in the store directory. Note that the path includes the name of the test (noop), and a timestamp. Jepsen also constructs a latest symlink at each level, so we can quickly inspect the most recent test results.

$ cat store/latest/results.edn
{:valid? true, :configs ({:model {}, :pending []}), :final-paths ()}

With the groundwork in place, we'll write the code to set up and tear down the database

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close