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: Using given & expect To Replace Scenarios

11.14.2012
| 2047 views |
  • submit to reddit

 The functionality in expectations.scenarios was borne out of compromise. I found certain scenarios I wanted to test, but I wasn't sure how to easily test them using what was already available in (bare) expectations. The solution was to add expectations.scenarios, and experiment with various features that make testing as easy as possible.

Two years later, the features that make sense have migrated back to expectations:

With those features, you should be able to convert any existing scenario to a bare expectation. What isn't covered with those features is what you should do if your scenario ends with multiple expects. This blog entry demonstrates how you can use given with a bare expectation to achieve the same test coverage.

Below is an example of a scenario that ends with multiple expects.
(ns blog-expectations
  (:use expectations.scenarios))

(scenario
 ;;; real tests will call domain code,
 ;;; I avoid that here for simplicity

 (let [x [1 2]
       y (map-indexed vector x)]
   (expect vector? x)
   (expect [0 1] (first y))
   (expect java.util.List y)))
Using given, these scenarios are actually very easy to convert. The given + bare expectation example below tests exactly the same logic.
(ns blog-expectations
  (:use expectations))

(given [expected actual]
       (expect expected
               (let [x [1 2]
                     y (map-indexed vector x)]
                 actual))
       vector? x
       [0 1] (first y)
       java.util.List y)
The test coverage is the same in the second example, but it is important to note that the let will now be executed 3 times instead of 1. This isn't an issue if your tests run quickly, if they don't you may want to revisit the test to determine if it can be written in a different way.

An interesting side-effect occurred while I was converting my scenarios - I found that some of my scenarios could be broken into multiple expectations that were then easier to read and maintain.

For example, the above expectations could be written as the example below.
(ns blog-expectations
  (:use expectations))

(expect vector? [1 2])

(given [x y-fn] (expect x
                        (y-fn
                         (map-indexed vector [1 2])))
       [0 1] first
       java.util.List identity)
note: you could simplify even further and remove the given, but that's likely only due to how contrived the test is. Still, the possibility exists that some scenarios will be easily convertible to bare expectations.

Using the technique described here, I've created bare expectations for all of the scenarios in the codebase I'm currently working on - and deleted all references to expectations.scenarios.



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