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
;; ...