Archimedes is a set of Hy macros that are used to make writing tests with
Hypothesis easier. fact
specifies a test case, variants
and
sample
specify rules for test data generation, profile
specifies
test settings, background
and with-background
specify common data
between tests. assert-macro-error
checks that macro-error
is
called with given message during macro expansion. For regular errors and
exceptions, use assert-error
.
In case you're using PyHamcrest, there's two macros to help you define
matchers: defmatcher
and attribute-matcher
. And for Hymn, there
is assert-right
.
For interactive mode, it's sometimes easier to execute fact
immediately. For this case, use check
.
Only Hypothesis is installed as a dependency. Hamcrest and Hymn have to be installed separately.
For more comprehensive documentation, see http://archimedes.readthedocs.io/
Also, Archimedes was a Greek mathematician, physicist, engineer, inventor and astronomer.
(require [archimedes [background fact check defmatcher attribute-matcher
assert-macro-error assert-error assert-right
with-background]])
(import [hypothesis.strategies [integers]]
[hamcrest [assert-that]]
[math [pow]])
(fact "true is always true"
(assert True))
(background some-numbers
a 3
b 4
c 5)
(fact "Pythagorean theorem holds in this specific case"
(with-background some-numbers [a b c]
(assert (= (+ (pow a 2) (pow b 2)) (pow c 2)))))
(fact "sum of two positive numbers is larger than either one of them"
(variants :a (integers :min-value 1)
:b (integers :min-value 1))
(assert (> (+ a b) a))
(assert (> (+ a b) b)))
(fact "example can clarify things"
(variants :a (integers :min-value 0 :max-value 10)
:b (integers :min-value 0 :max-value 10))
(sample :a 0 :b 0)
(assert (<= 0 (+ a b) 20)))
(fact "profile controls test settings"
(variants :a (integers :min-value 0))
(profile :max-examples 500)
(assert (<= 0 a)))
(fact "macro errors can be asserted"
(assert-macro-error "cond branches need to be a list"
(cond (= 1 1) True)))
(fact "even fact can be asserted for macro errors"
(assert-macro-error "too many variants forms"
(fact "I'm incorrect"
(variants :a (integers))
(variants :a (integers))
(assert (= a a)))))
(fact "errors can be asserted"
(assert-error "error"
(raise (ValueError "error"))))
(check "this is executed immediately"
(assert (= 1 1)))
(defmatcher is-zero? []
:match? (= item 0)
:match! "a zero"
:no-match! (.format "was a value of {0}" item))
(assert-that 0 (is-zero?))
(attribute-matcher item-with-length?
len =
"an item with length {0}"
"was an item with length {0}")
(assert-that "foo" (is- (item-with-length? 3)))
(background name elements)
defines setup function. Name is symbol. Name
of the test function will be "setup_" + name
. elements
is a list
of alternating symbols and their values. The setup function will return a
dictionary with keywordified symbols as keys and corresponding values as
their values.
(fact description code)
specifies a test function. description
is a
string describing what the test is about. The generated function will have a
name "test_" + description
and no arguments. Docstring of the function
will be value of description
. code
can be one or more forms of code,
they are inserted inside of the test function as is.
(check description code)
works just like fact
, except that the
resulting test function is immediately executed. This is useful when working
in interactive envinroment, like Jupyter or Hy repl.
(with-background name symbols code)
generates a let binding with code to call
background specified by name
. symbols
is list of symbols that should
be retrieved from dictionary returned by setup function and bound to local
context. code
is one or more elements of code, used to test things.
(variants keyword specification)
is used to specify test data that should
be generated by Hypothesis. It accepts arbitrary, but even, amount of
parameters. First specifies keywordified symbol and second strategy used to
generate value. If this form is present, test function's parameter list is
modified to have named parameters specified by keywords and is also wrapped
in given
decorator.
(sample keyword value)
specifies sample set of values. Keyword specifies
symbol and value holds the value bound to it. It should have same amount of
keywords as variants
form and can't be used without variants
form.
(profile keyword value)
specifies test settings. They match directly to
parameters given to settings
decorator.
(assert-macro-error message code)
asserts that during macro expansion of
code
an error is raised with a message of message
.
(assert-error message code)
asserts that code raises an error, which
string representation is equal to message.
(def-matcher name parameters :match? code :match! string :no-match string)
is used to create matcher function for hamcrest library. The resulting
matcher can then be used in assertions. Since the macro creates a behind the
scenes class, all parameters passed to it are accessible as instance
attributes. In match?
, match!
and no-match!
blocks, symbol
item
is bound to item currently under comparison.
(defmatcher length-of? [value] :match? (= (len item) self.value) :match! (.format "an item with length of {0} self.value) :no-match (.format "was an item with length of {0}" (len item))) (assert-that value (is- (lenght-of? 5)))
(attribute-matcher name function predicate string string)
is a special
case for matcher, where function is used to check a value of some matched
item and then compared to given value using predicate. Thus, the previous
example can be written as:
(attribute-matcher length-of? len = "an item with length of {0}" "was an item with length of {0}") (assert-that value (is- (length-of? 5)))
assert-right
is used with Hymn library's Either
monad. It first
checks that right
was returned as a result of computation and then
proceeds to run assertion block:
(assert-right (do-monad [status (advance-time-m society)] status) (assert-that society (has-less-resources-than? old-resources)))
Archimedes is geared towards Nose, but it might work with other frameworks that rely on naming conventions to discover tests to be executed.
Licensed under MIT license