Domain Driven Architecture

Data-driven unit testing with data-test

Autor: Lukas Schondorff
August 29, 2019

Tags: clojure, data-test, howto, unit testing, dda-pallet

We at DomainDrivenArchitecture do a lot of testing and we really appreciate test driven development. With test driven development we get good shaped APIs and ensure our projects are working as intended. As we eat our own dog food we love to have happy and satisfied customers.

But there are also downsides of doing test first. Our DevOps projects (for example dda-serverspec-crate) consists of large config maps. As a result, the maps representing input and expected output data of the tests were getting bigger and therefore hard to read. Only a small amount of the test code was actually calling the function under test.

Fixing bugs or adjusting tests for new features was consuming more time than it should because naviagation and orientation in large test files was hard. In order to improve this situation and coming back to joy of doing test driven development we created data-test, a test framework which separates test data from test code. data-test is compatible to clojures test environment so we are able to use our test setups (e.g. lein test) without changes.

Lets take a look at one of our own projects, before adjusting our way of testing:

This is only one test out of many, and it already starts to look a little unreadable. Just imagine having up to ten of those tests in one file and you start feeling our pain point.

data-test

As you already imagined by the name and given example, data-test is a test framework for organizing tests. But what does that exactly mean? For now, lets take a look at the whole exact same test namespace as given above after applying data-test:

As you can see, the whole namespace shrank down to 17 lines of code. And this is one of the most important things, because now you only have code inside of our test namespace and all the in- and output is sourced out to external files. This way, you can easily see, which test you want to adjust and then open the corresponding file (an .edn file). If we take a closer look at our test example

(ns dda.pallet.dda-serverspec-crate.domain-test
  (:require
   [clojure.test :refer :all]
   [data-test :refer :all]
   [dda.pallet.dda-serverspec-crate.domain :as sut]))

(defdatatest should-create-infra-configuration [input expected]
    (is (= expected
           (sut/infra-configuration input))))

Data location convention

The file containing datas location is determined by test namespace and tests name.

Data file location in detail:

  • The namespace is mapped to a directory according to clojures naming conventions.
  • Test name is mapped to the data file name.
  • Optionally the data files can contain numbers .[0-9]. in order to test multiple data sets against one testcase.
  • Test data is read in .edn format. In addition we support notation defined in aero.

As test data is load as resource from class path, data-test will also work in uberjars.

Data format

Files containing test data have to contain two elements input and expected

{:input 1
 :expected 2}

Values may be simple primitives, collections or maps. data-test support aero notations in order to be able to reference data parts used in more than one location.

Beside of aero notation we decided to keep test data as stupid and static as possible.

Binding in test functions

Input and expectation defined contents in data files are bound by the binding definition part: [input expected]

The symbols input and expected can be used in the test code to further define the tests.

Execute test

At first, you need to add [dda/data-test "0.1.0"] to your dependencies e.g. in the profiles.clj.

Comparable to simple clojure tests you can execute data-test with (clojure.test/run-tests). The result will look like:

Running namespace tests…
FAIL in: project_test.clj: 35:
    should-test-increment: project_test/should_test_increment.1.edn:
  expected: 2

  actual: (1)


4 tests finished, problems found. 😭 errors: 0, failures: 1, ns: 1, vars: 3

As you can see, in addition to the test output the used input file and test name are provided. That is quite useful for tracking down issues.

data-test improves testing

data-test helps to keep your unit tests structured and clean by allowing you to better organize test specifications in separate files.

If you want a deeper look at data-test you can visit our GitHub page at https://github.com/DomainDrivenArchitecture/data-test.

If you like our idea we will be happy about your star or comments. In case we missed something feel free to provide feedback, contribute or share your ideas with us.

Issues and questions are welcome at github or clojurians slack.

Stay in touch at our mastodon presence.