Skip to content

tuturto/archimedes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hy macros for Hypothesis framework

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.

Examples are good:

(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)))

Details are needed sometimes:

(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)))

Note about test framework:

Archimedes is geared towards Nose, but it might work with other frameworks that rely on naming conventions to discover tests to be executed.

License:

Licensed under MIT license

About

Hy macros for Hypothesis framework

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published