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