Clojure: Expectations Verify Interaction Args
The expectations framework provides the ability to create interaction (or behavior) based tests. I've previously written about adding interaction based testing to expectations;
however, the examples from that blog entry focused exclusively on
testing interactions where each argument is matched using equality. In
this entry I'll give examples of how each argument can be also be
verified using a class, regex, exception, or a custom function.
When writing state based tests
using expectations the type of test you're writing is inferred from the
expected value. If the expected value is a regex, expectations will
test the actual value to see if it matches the regex. If you passed in a
class, expectations will test the actual value to see if it's an
instance of that class. If you passed in an exception... you get the
idea. All of what I said above, is also true for arguments of an
interaction.
Let's start with a simple interaction based test:
(ns blog-expectations (:use expectations)) (expect (interaction (spit "/tmp/hello-world" "some data" :append true)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append true)))
In the example above, we're calling the spit function with exactly the
arguments that we've specified in our test. This test will pass;
however, we've had to specify the exact file location and the exact
data. If for some reason you can't specify exactly what the argument
will be, it's nice to have a way to specify as much as you possibly can.
In the example below, we're still specifying the exact data, but we're only verifying that the file is somewhere in /tmp/.
(expect (interaction (spit #"/tmp/" "some data" :append true)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append true)))
As I previously mentioned, we can also get more general and only verify
the class of an argument. For example, if we knew our data was going to
be a String, but we didn't want to specify exactly what that string was,
the following test would do the trick.
(expect (interaction (spit #"/tmp/" String :append true)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append true)))
While expectations provides you with a lot of default options, there are
times when you'll want to write your own argument "matcher". As a
contrived example, let's pretend that we want to test that the last
argument is true or nil.
(defn true-or-nil? [x] (or (true? x) (nil? x))) ;; this test passes (expect (interaction (spit #"/tmp/" String :append true-or-nil?)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append true))) ;; so does this test (expect (interaction (spit #"/tmp/" String :append true-or-nil?)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append nil))) ;; this test fails (expect (interaction (spit #"/tmp/" String :append true-or-nil?)) (do (spit "/tmp/somewhere-else" "nil") (spit "/tmp/hello-world" "some data" :append "not true or nil")))
One of the best features of expectations is it's error reporting, and
the same error reporting logic is applied to arguments when an
interaction based test fails. Given the example above, you'll get the
following error message.
failure in (success_examples.clj:204) : success.success-examples
expected: (spit #"/tmp/" String :append true-or-nil?)
got: 0 times
-- got: (spit "/tmp/somewhere-else" "nil")
"nil", "/tmp/somewhere-else" are in actual, but not in expected
true_or_nil_QMARK, #"/tmp/", :append, String are in expected, but not in actual
expected is larger than actual
-- got: (spit "/tmp/hello-world" "some data" :append "s")
- arg4: not true or nilAs you can see both calls are reported, and each argument has a detailed report (if it did not match).
Finally, expectations provides and additional function that can be used
to verify that certain key/value pairs are in an argument. The following
example doesn't really make sense, since you'd never want to pass a map
as the last argument to spit, but it's easy to follow in the context of
this blog entry.
(expect (interaction (spit String #"some da" keyword? (contains-kvs :a :b :c :d)))
(spit "/tmp/hello-world" "some data" :append {:a :b :c :d :e :f}))In the above example, (contains-kvs) is used to verify that the final argument to spit contains the key/value pairs :a :b :c :d.
I hope that interaction arg matching follows the principle of least
surprise, since it behaves the same as expectations state based tests. I
also hope that the ability to use an arbitrary function for
verification will provide any necessary flexibility. If you're using
expectations, give it a try and let me know.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





