Mocking Clojure Functions with Atticus

I dislike most mocking, and try and avoid it as much as possible. Sometimes it is however the only realistic way of testing. I did a quick survey of mocking tools in clojure, and found them very much reflecting the Java mocking libraries. Clojure has a few more dynamic capabilities than Java, so I thought a little about how these could be used to make a simple mocking facility, and atticus is what I came up with.

There is a consensus that mocking should be implemented by binding a function's var to a new function for the duration of a test, and atticus does this too. Atticus' premise is that we can do simple mocking by declaring the mock function as a local function definition, and have the local function do the argument checking, return value setting, etc. The simplest case would be something like below, which checks the value of its argument and specifies a return value:

 ;; pull in namespaces (use 'clojure.test) (require 'atticus.mock)

;; define test which mocks f (deftest mock-test (atticus.mock/expects [(f [arg] (is (= arg 1) "Check argument") arg)] ; set the return value ;; in a real test case this would be called ;; indirectly by some other function (is (= 1 (f 1)) "Call mocked function"))

At the moment, I have added two macros to this. once, which checks a function is called once, and times, which checks that a function is called a specific number of times. The macros are used to wrap the body of the mock function, which keeps the function's expected behaviour in one place.

 ;; define test, that should be called just once (deftest mock-test   (atticus.mock/expects     [(f [arg]        (atticus.mock/once          (is (= arg 1) "Check argument")          arg))]     (is (= 1 (f 1)) "Call mocked function")) 
 ;; define test, that should be called exactly twice (deftest mock-test   (atticus.mock/expects     [(f [arg]        (atticus.mock/times 2          (is (= arg 1) "Check argument")          arg))]     (is (= 1 (f 1)) "Call mocked function")     (is (= 1 (f 1)) "Call mocked function")) 

So what do you think, is this a reasonable approach? Not having the explicit calls to returns, etc, might be seen as a loss of declarative clarity, but I for one prefer this, as it gives you the full power of the language to test the arguments and set the return value.

References

Discuss this post here.

Published: 2010-05-18

Shell Scripting in Clojure with Pallet

Let's face it, many of us hate writing shell scripts, and with good reason. Personally, it's not so much the shell language itself that puts me off, but organising everything around it; how do you deploy your scripts, how do you arrange to call other scripts, how do you manage the dependencies between your scripts? Pallet aims to solve these problems by embedding shell script in clojure.

Embedding in Clojure

Embedding other languages in lisp is not a new idea; parenscript, scriptjure (which Pallet's embedding is based on), and ClojureQL all do this.

So what does shell script in clojure look like? Some examples:

(script   (ls "/some/path")   (defvar x 1)   (println @x)   (defn foo [x] (ls @x))   (foo 1)   (if (= a a)     (println "Reassuring")     (println "Ooops"))   (println "I am" @(whomai))

which generates:

ls /some/path x=1 echo ${x} function foo(x) { ls ${x}  } foo 1 if [ \( \"a\" == \"a\" \) ]; then echo Reassuring;else echo Ooops;fi echo I am $(whomai) 

The aim is to make writing shell script similar to writing Clojure, but there are obvious differences in the language that limit how far that can be taken. To run the code above at the REPL, you'll have to use the pallet.stevedore package.

Escaping back to Clojure

Escaping allows us to embed Clojure values and expressions inside our scripts, in much the same way as symbols can be unquoted when writing macros.

(let [path "/some/path"]   (script     (ls ~path)     (ls ~(.replace path "some" "other)))

We can now write Clojure functions that produce shell scripts. Writing scripts as clojure functions allows you to use the Clojure namespace facilities, and allows you to distribute you scripts in jar files (which can be deployed in a versioned manner with maven)

(defn list-path [path]   (script     (ls ~path)     (ls ~(.replace path "some" "other)))

Composing scripts

Pallet allows the scripts to be combined. do-script concatenates the code pieces together.

(do-script   (list-path "path1")   (list-path "path2")) 

chain-script chains the scripts together with '&&'.

(chain-script   (list-path "path1")   (list-path "path2")) 

checked-script finally allows the chaining of scripts, and calls exit if the chain fails.

(checked-script "Message"   (list-path "path1")   (list-path "path2")) 

Conclusion

Writing shell script in Clojure gives access to Clojure's namespace facility allowing modularised shell script, and to Clojure's packaging as jar files, which allows reuse and distribution. The ability to compose script fragments leads to being able to macro-like functions, such checked-script, and you could even use Clojure macros to generate script (but I haven't thought of a use for that, yet).

The syntax for the embedding has arisen out of practical usage, so is far from complete, and can definitely be improved. I look forward to hearing your feedback!

UPDATE: stevedore now requires a binding for template, to specify the target for the script generation. This should be a vector containing one of :ubuntu, :centos, or :darwin, and one of :aptitude, :yum, :brew.

Discuss this post here.

Published: 2010-05-03

Swank Clojure gets a Break with the Local Environment

Recently I got fed up with a couple of warts in swank-clojure, so I made a couple of small fixes, and that lead to a couple of new features. Using SLIME with Clojure has never been as smooth as using it with Common Lisp, and the lack of debug functionality beyond the display of stack traces is particularly onerous. Recently, George Jahad and Alex Osborne's debug-repl showed the possibility of adding a break macro to enter the debugger with the call stack intact and local variables visible. This functionality is now in swank-clojure.

Consider the following example, adapted from debug-repl:

   (let [c 1
        d 2]     (defn a [b c]       (swank.core/break)       d))   (a "foo" "bar") 

Running this now brings up the following SLDB debug frame:

 BREAK:   [Thrown class java.lang.Exception]

Restarts: 0: [QUIT] Quit to the SLIME top level 1: [CONTINUE] Continue from breakpoint

Backtrace: 0: user$eval1666$a1667.invoke(NOSOURCEFILE:1) 1: user$eval__1670.invoke(NOSOURCEFILE:1) 2: clojure.lang.Compiler.eval(Compiler.java:5358) 3: clojure.lang.Compiler.eval(Compiler.java:5326) 4: clojure.core$eval__4157.invoke(core.clj:2139) –more–

As you can see, the stack trace reflects the location of the breakpoint, and there is a CONTINUE restart. Pressing "1", or Enter on the CONTINUE line, or clicking the CONTINUE line should all cause the the debugger frame to close, and the result of the function call, 2, to be displayed in the REPL frame.

Enter, or "t", on the first line of the stacktrace causes the local variables to be displayed:

 BREAK:   [Thrown class java.lang.Exception]

Restarts: 0: [QUIT] Quit to the SLIME top level 1: [CONTINUE] Continue from breakpoint

Backtrace: 0: user$eval1666$a1667.invoke(NOSOURCEFILE:1) Locals: b = foo d = 2 c = bar 1: user$eval__1670.invoke(NOSOURCEFILE:1) 2: clojure.lang.Compiler.eval(Compiler.java:5358) 3: clojure.lang.Compiler.eval(Compiler.java:5326) 4: clojure.core$eval__4157.invoke(core.clj:2139) –more–

Pressing enter on one of the local variable lines will pull up the SLIME inspector with that value. If you go back to the REPL without closing the SLDB frame, there will be no prompt, but pressing enter should give you one. The local variables are then all be avaiable for evaluation form the REPL.

Should an error occur while you are using the REPL, you will be placed in a nested debug session, with an "ABORT" restart to return to the previous debug level.

Finally, restarts are now displayed for each of the exceptions in the exception cause chain.

   (let [c 1
        d 2]     (defn a [b c]       (throw (Exception. "top" (Exception. "nested" (Exception. "bottom"))))       d))   (a "foo" "bar") 

This will bring up the debugger with 2 cause restarts, which can be used to examine the related stack traces.

 top    [Thrown class java.lang.Exception]

Restarts: 0: [QUIT] Quit to the SLIME top level 1: [CAUSE1] Invoke debugger on cause nested [Thrown class java.lang.Exception] 2: [CAUSE2] Invoke debugger on cause bottom [Thrown class java.lang.Exception]

Backtrace: 0: user$eval1752$a1753.invoke(NOSOURCEFILE:1) 1: user$eval__1756.invoke(NOSOURCEFILE:1) 2: clojure.lang.Compiler.eval(Compiler.java:5358) 3: clojure.lang.Compiler.eval(Compiler.java:5326) 4: clojure.core$eval__4157.invoke(core.clj:2139) –more–

The break functionality is known only to work from the REPL thread at the moment. With that small proviso, I hope you enjoy the new functionality - at least it provides a basic debug functionality until full JPDA/JDI integration is tackled.

Discuss this post here.

Published: 2010-03-31

Archive