Jay Fields is a software developer at DRW Trading. He has a passion for discovering and maturing innovative solutions. His most recent work has been in the Domain Specific Language space where he's delivered applications that empowered subject matter experts to write the business rules of the applications. He is also very interested in maturing software design through software testing. Jay is a DZone MVB and is not an employee of DZone and has posted 116 posts at DZone. You can read more from them at their website. View Full User Profile

Clojure: Interaction Based Testing Added To Expectations

11.10.2012
| 1824 views |
  • submit to reddit

 The vast majority of testing I do these days is state-based; however, there are times when I need to test an interaction (e.g. writing to a file or printing to standard out). The ability to test interactions has been in expectations.scenarios for quite awhile, but there isn't any reason that you need a scenario to test an interaction - so, as of version 1.4.16, you also have the ability to test interactions with bare expectations.

The following test shows how you can specify an expected interaction. This test passes.

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

Writing the test should be straightforward - expect the interaction and then call the code that causes the interaction to happen.

As I was adding this behavior I enhanced the error reporting. Below you can find a failing test and the output that is produced.

(expect (interaction (one "hello" {:a :b :c {:dd :ee :ff :gg}}))
        (do
          (one "hello")
          (one "hello" "world" "here")
          (one "hello" {:a 1 2 3})))

 ; expected: (one "hello" {:a :b, :c {:ff :gg, :dd :ee}}) once
 ;
 ; got: (one "hello")
 ; {:a :b, :c {:ff :gg, :dd :ee}} are in expected, but not in actual
 ; expected is larger than actual
 ;
 ; got: (one "hello" "world" "here")
 ; "here", "world" are in actual, but not in expected
 ; {:a :b, :c {:ff :gg, :dd :ee}} are in expected, but not in actual
 ; actual is larger than expected
 ;
 ; got: (one "hello" {2 3, :a 1})
 ; arg1: matches
 ; expected arg2: {:a :b, :c {:ff :gg, :dd :ee}}
 ; actual arg2: {2 3, :a 1}
 ; 2 with val 3 is in actual, but not in expected
 ; :c {:dd with val :ee is in expected, but not in actual
 ; :c {:ff with val :gg is in expected, but not in actual
 ; :a expected: :b
 ; was: 1


As you can see, all three calls to the 'one' function are reported. If the number of args used to call 'one' are of the same size as the expected args, each arg is compared in detail; otherwise the two lists are compared in detail (but the elements are not).

As you can see in this failure the first argument, "hello", matches.

;          got: (one "hello" {2 3, :a 1})
 ;                   arg1: matches
 ;          expected arg2: {:a :b, :c {:ff :gg, :dd :ee}}
 ;            actual arg2: {2 3, :a 1}
 ;          2 with val 3 is in actual, but not in expected
 ;          :c {:dd with val :ee is in expected, but not in actual
 ;          :c {:ff with val :gg is in expected, but not in actual
 ;          :a expected: :b
 ;                  was: 1

Anytime an argument matches expectations will simply print "matches". You can also specify :anything as an argument, to ignore that argument and always 'match'. The following test shows an example of matching the second argument, while the first argument is no longer matching. 

(expect (interaction (one "hello" :anything))
        (one "help" {:a 1 2 3}))

; expected: (one "hello" :anything) once
;
; got: (one "help" {2 3, :a 1})
; expected arg1: hello
; actual arg1: help
; matches: "hel"
; diverges: "lo"
; &: "p"
; arg2: matches

That's it. Hopefully these interaction tests follow the principle of least surprise, and are easy for everyone to use.

Published at DZone with permission of Jay Fields, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)