Date: 27 November 2018
Version: 3.0.2
1 Introduction
hara.test is a test framework based off of the midje syntax, containing macros and helpers for easy testing and verification of functions
1.1 Installation
Add to project.clj dependencies:
[hara/test "3.0.2"]
All functions are in the hara.test namespace.
(use (quote hara.test))
2 General
-main ^
main entry point for leiningen
(-main)
print-options ^
output options for test results
(print-options)
=> #{:disable :default :all :current :help}
(print-options :default)
=> #{:print-bulk :print-failure :print-thrown}
(run :list)
(run 'hara.core.base.util)
;; {:files 1, :thrown 0, :facts 8, :checks 18, :passed 18, :failed 0}
=> map?
run-errored ^
runs only the tests that have errored
(run-errored)
3 Checker
all ^
checker that allows `and` composition of checkers
(mapv (all even? #(< 3 %))
[1 2 3 4 5])
=> [false false false true false]
any ^
checker that allows `or` composition of checkers
(mapv (any even? 1)
[1 2 3 4 5])
=> [true true false true false]
anything ^
a checker that returns true for any value
(anything nil) => true
(anything [:hello :world]) => true
approx ^
checker that allows approximate verifications
((approx 1) 1.000001) => true
((approx 1) 1.1) => false
((approx 1 0.0000001) 1.001) => false
contains ^
checker for maps and vectors
((contains {:a odd? :b even?}) {:a 1 :b 4})
=> true
((contains {:a 1 :b even?}) {:a 2 :b 4})
=> false
((contains [1 2 3]) [1 2 3 4])
=> true
((contains [1 3]) [1 2 3 4])
=> false
contains-in ^
shorthand for checking nested maps and vectors
((contains-in {:a {:b {:c odd?}}}) {:a {:b {:c 1 :d 2}}})
=> true
((contains-in [odd? {:a {:b even?}}]) [3 {:a {:b 4 :c 5}}])
=> true
exactly ^
checker that allows exact verifications
((exactly 1) 1) => true
((exactly Long) 1) => false
((exactly number?) 1) => false
is-not ^
checker that allows negative composition of checkers
(mapv (is-not even?)
[1 2 3 4 5])
=> [true false true false true]
just ^
combination checker for both maps and vectors
((just {:a odd? :b even?}) {:a 1 :b 4})
=> true
((just {:a 1 :b even?}) {:a 1 :b 2 :c 3})
=> false
((just [1 2 3 4]) [1 2 3 4])
=> true
((just [1 2 3]) [1 2 3 4])
=> false
((just [3 2 4 1] :in-any-order) [1 2 3 4])
=> true
just-in ^
shorthand for exactly checking nested maps and vectors
((just-in {:a {:b {:c odd?}}}) {:a {:b {:c 1 :d 2}}})
=> false
((just-in [odd? {:a {:b even?}}]) [3 {:a {:b 4}}])
((just-in [odd? {:a {:b even?}}]) [3 {:a {:b 4}}])
=> true
satisfies ^
checker that allows loose verifications
((satisfies 1) 1) => true
((satisfies Long) 1) => true
((satisfies number?) 1) => true
((satisfies #{1 2 3}) 1) => true
((satisfies [1 2 3]) 1) => false
((satisfies number?) "e") => false
((satisfies #"hello") #"hello") => true
throws ^
checker that determines if an exception has been thrown
((throws Exception "Hello There")
(result/map->Result
{:status :exception
:data (Exception. "Hello There")}))
=> true
throws-info ^
checker that determines if an `ex-info` has been thrown
((throws-info {:a "hello" :b "there"})
(common/evaluate {:form '(throw (ex-info "hello" {:a "hello" :b "there"}))}))
=> true
4 Walkthrough
4.1 Running
Tests can be run in the repl:
> lein run -m hara.test :exit
4.2 Basics
For those that are familiar with midje, it's pretty much the same thing. We define tests in a fact expression:
(fact "lets test to see which if numbers are odd"
1 => odd?
2 => odd?)
;; Failure [hara_test.clj]
;; Info "lets test to see which if numbers are odd"
;; Form 2
;; Check odd?
;; Actual 2
Or for the more grammatically correct, a facts expression:
(facts "lets test to see which if numbers are even?"
1 => even?
2 => even?)
;; Failure [hara_test.clj]
;; Info "lets test to see which if numbers are even?"
;; Form 1
;; Check even?
;; Actual 1
The arrow => forms an input/output syntax and can only be in the top level fact form. This means that it cannot be nested arbitrarily in let forms as in midje. This has the effect simplifying the codebase as well as forcing each individual test to be more self-sufficient. The arrow is flexible and is designed so that the input/output syntax can be kept as succinct as possible. Other checks that can be performed are given as follows:
(facts "Random checks that show-off the range of the `=>` checker"
;; check for equality
1 => 1
;; check for being in a set
1 => #{1 2 3}
;; check for class
1 => Long
;; check for function
1 => number?
;; check for pattern
"one" => #"ne")
4.3 Metadata
Metadata can be placed on the fact/facts form in order to provide more information as to what exactly the fact expression is checking:
^{:refer hara.test/fact :added "2.4" :tags #{:api}}
(fact "adding metadata gives more information"
(+ 1 2 3) => (+ 3 3))
Metadata allows the test framework to quickly filter through what test are necessary, as well as to enable generation of documentation and docstrings through external tools.
4.4 Options
Options for run and run-namespace include:
- specifing the
:test-paths option (by default it is "test") - specifing
:include and :exclude entries for file selection - specifing
:check options::include and :exclude entries::tags so that only the :tags that are there on the meta data will run.:refers can be a specific function or a namespace:namespaces refers to specific test namespaces
- specifing
:print options for checks
Some examples can be seen below:
(run {:checks {:include [{:tags #{:web}}]} ;; only test for :web tags
:test-paths ["test/hara"]}) ;; check out "test/hara" as the main path
=> {:files 0, :thrown 0, :facts 0, :checks 0, :passed 0, :failed 0}
Only test the hara.time-test namespace
(run {:checks {:include [{:namespaces #{'hara.time-test}}]}})
=> {:files 1, :thrown 0, :facts 32, :checks 53, :passed 53, :failed 0}
;; Summary (1)
;; Files 1
;; Facts 32
;; Checks 53
;; Passed 53
;; Thrown 0
;;
;; Success (53)
Only test facts that refer to methods with hara.time namespace:
(run {:test-paths ["test/hara"]
:checks {:include [{:refers '#{hara.time}}]}})
=> {:files 1, :thrown 0, :facts 32, :checks 53, :passed 53, :failed 0}
;; Summary (1)
;; Files 1
;; Facts 32
;; Checks 53
;; Passed 53
;; Thrown 0
;;
;; Success (53)
Only pick one file to test, and suppress the final summary:
(run {:test-paths ["test/hara"]
:include ["^time"]
:print #{:print-facts}})
=> {:files 8, :thrown 0, :facts 54, :checks 127, :passed 127, :failed 0}
;; Fact [time_test.clj:9] - hara.time/representation?
;; Info "checks if an object implements the representation protocol"
;; Passed 2 of 2
;; Fact [time_test.clj:16] - hara.time/duration?
;; Info "checks if an object implements the duration protocol"
;; Passed 2 of 2
;; ...