Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help in designing a small Unit Test Macro in Clojure

I have a small unit test macro

  (defmacro is [expr value]
        `(if (= ~expr ~value)
             'yes
             (println "Expected " ~value "In" ~expr "But Evaluated to" ~expr)))

How do I correctly write the error message? Right now it gives

Clojure 1.0.0-
1:1 user=> (is (+ 2 2) 4)
user/yes
1:2 user=> (is (+ 2 2) 5)
Expected  5 In 4 But Evaluated to 4

I want to evaluate the expression and say 'yes if the evaluated value matches expected else print the error with the unevaluated expression instead of the value after "In".

like image 799
unj2 Avatar asked Aug 08 '09 14:08

unj2


1 Answers

Original Answer

I think this is what you are looking for:

(defmacro is [expr value]
    `(if (= ~expr ~value)
         true
         (println "Expected " ~value "In" (str (quote ~expr)) 
                  "But Evaluated to" ~expr)))

what (quote ~expr) does is bring the s-expression represented by expr into your "template", but prevents it from being evaluated, then applying str to the unevaluated s-expression makes it into a string for concatenation with the other strings of the error message. So, :

user=> (is (+ 2 2) 5)
Expected  5 In (+ 2 2) But Evaluated to 4

produces the desired behavior.

Rather than using 'yes to report success, you can simply use true as the report for the success of the test, giving:

user=> (is (+ 2 2) 4)
true

and avoiding the problem of 'yes being returned as a qualified symbol. If you need a symbol for some reason, then you could make a keyword:

(defmacro is [expr value]
    `(if (= ~expr ~value)
         :yes
         (println "Expected " ~value "In" (str (quote ~expr)) 
                  "But Evaluated to" ~expr)))


user=> (is (+ 2 2) 4)
:yes



Answer to Question Asked in Comments

You asked in comments:

I am sorry I should have been clearer in my question. What would be the difference between a symbol and keyword in this case?

Consider your original definition (with the correction to make the error return the way you wanted):

(defmacro is [expr value]
`(if (= ~expr ~value)
     'yes
     (println "Expected " ~value "In" (str (quote ~expr)) 
              "But Evaluated to" ~expr)))

I imagine that you want the 'yes thinking that you could use to test against 'yes in other contexts (one often sees these types of tests in introductory lisp texts). But 'yes is used in your macro definition, it returns a qualified symbol when the test is passes:

user=> (is (+ 2 2) 4)
user/yes

Now, this is not what you would want. Imagine that you said this:

user=> (= 'yes (is (+ 2 2) 4))
false

To get a true answer, you would have to say this (using a syntax quote):

user=> (= `yes (is (+ 2 2) 4))
true

If you defined your macro to return :yes, then you get the checkable return without having to syntax quote the object you are using to do the checking:

user=> (= :yes (is (+ 2 2) 4))
true

But this is all superfluous because you are really interested in whether (+ 2 2) returns 4, i.e. whether your assertion is true or false rather than whether 'yes equals ``yesor'yes; you can just have it returntrue` and avoid this checking step (in the real world).

In any case, to understand what qualified symbols are, you need to read the docs on the clojure reader and this SO answer explaining the ins and outs of symbols in clojure.

like image 58
Pinochle Avatar answered Nov 15 '22 09:11

Pinochle