0

Put simply, I would like the code shown below to behave how one could think it should behave, e.g. defining the left-hand side value (here as a trivial anonymous associative collection) just once and applying multiple different arrow functions (asserions) to it in a clean and idiomatic way:

  (deftest test-test
    (fact "some-fact"
      {:a 1 :b 2}
      =>
          (contains {:a odd?}))
      =not=>
          (contains {:a even?})
    )

This code obviously doesn't work: only => arrow actually asserts and =not=> is irrelevant (e.g. if we change even? to odd? there the test still passes).

Maybe it's as simple as wrapping in let or using def, but being new to Clojure I would like to know what is the preferred way.

Michal M
  • 1,521
  • 14
  • 35
  • If you're new to Clojure, I'd strongly recommend avoiding Midje. It is not idiomatic, it is not widely used, tooling/editor support for it isn't great these days, and you'll have a hard time getting support since it's mostly abandoned at this point (the original maintainer moved on to another tech). – Sean Corfield Sep 29 '22 at 22:39
  • @SeanCorfield that's good to know! Unfortunately, it is a part of a legacy codebase I have to deal with. While at it, and while being OK with a purely opinion based answer, what would you consider a state-of-the-art testing framework for Clojure nowadays? – Michal M Sep 30 '22 at 04:54
  • 1
    At work we use a mixture of plain ol' `clojure.test` and `expectations.clojure.test`, which is a `clojure.test`-compatible version of the Classic Expectations library originally created by Jay Fields (which I now maintain, since Jay stepped away and we were committed to using it at that point). https://github.com/clojure-expectations/clojure-test I don't think Expectations is widely used but I like the expressiveness of it. We have about 26k lines of tests at work (out of 143k lines of Clojure in total). – Sean Corfield Oct 01 '22 at 05:17

1 Answers1

3

Every checkable must have a left-hand side, an arrow, and a right-hand side, so this code won't work. I also think that Midje is intended as the unit testing library (testing result of one function for different inputs) and you should instead use Spec or Malli for testing data structures.

That said, I think you can achieve that result in several ways:

You can of course repeat your left-hand side value twice:

(fact "some-fact"
      {:a 1 :b 2} => (contains {:a odd?})
      {:a 1 :b 2} =not=> (contains {:a even?}))
=> true

Or insert it into let:

(let [my-map {:a 1 :b 2}]
  (fact "some-fact"
        my-map => (contains {:a odd?})
        my-map =not=> (contains {:a even?})))
=> true

You can use every-checker:

(fact "some-fact"
      {:a 1 :b 2}
      => (every-checker (contains {:a odd? :b even?})
                        #(not (some-> (:a %) even?))))
=> true

You can write some macro, which would transform that code exactly as you wish (if you're really new to Clojure, think twice if you really need to do that):

(defmacro mfact [doc o & args]
  `(fact ~doc
         ~@(mapcat (fn [[x y]] (list o x y))
                   (partition 2 args))))

(mfact "some-fact"
       {:a 1 :b 2}
       =>
       (contains {:a odd?})
       =not=>
       (contains {:a even?}))
=> true
Martin Půda
  • 7,353
  • 2
  • 6
  • 13
  • Such a areat answer! I really appreciate you've shown the macro approach, while I will hold of with such wizardry for now, for a newbie it is certainly interesting to see them put to such a simple but pragmatic use. – Michal M Sep 30 '22 at 05:00