marick/Midje

Midje throws exception when using prerequisites for stubbing function with specific arguments

Closed this issue · 4 comments

clov0 commented

"Midje caught an exception when translating this form : "
happens when this code :

(fact "some fact"
          (setup-publisher publisher-desc) => anything
          (provided
            (lanex/declare anything anything anything anything)  => anything :times 1
            (lancore/close anything) => anything :times 2))

changes to this :

(fact "some fact"
          (setup-publisher publisher-desc) => anything
          (provided
            (lanex/declare anything (:name (:exchange publisher-desc)) (:type (:exchange publisher-desc)) anything)  => anything :times 1
            (lancore/close anything) => anything :times 2))

Midje 1.8.3, using default config.

Stack trace :

java.lang.Exception: Programmer error
midje.parsing.util.fnref$classify_function_reference.invokeStatic(fnref.clj:8)
midje.parsing.util.fnref$classify_function_reference.invoke(fnref.clj:7)
midje.parsing.2_to_lexical_maps.folded_fakes$mockable_funcall_QMARK_$mockable_function_QMARK___6961.invoke(folded_fakes.clj:38)
midje.parsing.2_to_lexical_maps.folded_fakes$mockable_funcall_QMARK_.invokeStatic(folded_fakes.clj:41)
midje.parsing.2_to_lexical_maps.folded_fakes$mockable_funcall_QMARK_.invoke(folded_fakes.clj:31)
midje.parsing.2_to_lexical_maps.folded_fakes$eval6968$folded_fake_QMARK___6989.invoke(folded_fakes.clj:59)
midje.parsing.2_to_lexical_maps.folded_fakes$unfolding_step.invokeStatic(folded_fakes.clj:77)
midje.parsing.2_to_lexical_maps.folded_fakes$unfolding_step.invoke(folded_fakes.clj:69)
midje.parsing.2_to_lexical_maps.folded_fakes$eval7031$unfold_expect_form__then__stay_put__7032.invoke(folded_fakes.clj:96)
midje.parsing.util.zip$translate_zipper.invokeStatic(zip.clj:29)
midje.parsing.util.zip$translate_zipper.doInvoke(zip.clj:21)
midje.parsing.2_to_lexical_maps.folded_fakes$eval7031$unfold_fakes__7038.invoke(folded_fakes.clj:100)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invokeStatic(facts.clj:148)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invoke(facts.clj:140)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invokeStatic(facts.clj:186)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invoke(facts.clj:182)
midje.sweet$fact$fn__7642.invoke(sweet.clj:192)
midje.parsing.util.error_handling$parse_and_catch_failure.invokeStatic(error_handling.clj:38)
midje.parsing.util.error_handling$parse_and_catch_failure.invoke(error_handling.clj:17)
midje.sweet$fact.invokeStatic(sweet.clj:184)
midje.sweet$fact.doInvoke(sweet.clj:170)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:107)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.util.laziness$eagerly.invokeStatic(laziness.clj:16)
midje.util.laziness$eagerly.invoke(laziness.clj:6)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:109)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invokeStatic(facts.clj:148)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invoke(facts.clj:140)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invokeStatic(facts.clj:186)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invoke(facts.clj:182)
midje.sweet$fact$fn__7642.invoke(sweet.clj:192)
midje.parsing.util.error_handling$parse_and_catch_failure.invokeStatic(error_handling.clj:38)
midje.parsing.util.error_handling$parse_and_catch_failure.invoke(error_handling.clj:17)
midje.sweet$fact.invokeStatic(sweet.clj:184)
midje.sweet$fact.doInvoke(sweet.clj:170)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:107)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.util.laziness$eagerly.invokeStatic(laziness.clj:16)
midje.util.laziness$eagerly.invoke(laziness.clj:6)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:109)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.util.laziness$eagerly.invokeStatic(laziness.clj:16)
midje.util.laziness$eagerly.invoke(laziness.clj:6)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:109)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invokeStatic(facts.clj:148)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invoke(facts.clj:140)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invokeStatic(facts.clj:186)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invoke(facts.clj:182)
midje.sweet$fact$fn__7642.invoke(sweet.clj:192)
midje.parsing.util.error_handling$parse_and_catch_failure.invokeStatic(error_handling.clj:38)
midje.parsing.util.error_handling$parse_and_catch_failure.invoke(error_handling.clj:17)
midje.sweet$fact.invokeStatic(sweet.clj:184)
midje.sweet$fact.doInvoke(sweet.clj:170)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:107)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.util.laziness$eagerly.invokeStatic(laziness.clj:16)
midje.util.laziness$eagerly.invoke(laziness.clj:6)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invokeStatic(facts.clj:109)
midje.parsing.1_to_explicit_form.facts$midjcoexpand.invoke(facts.clj:97)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invokeStatic(facts.clj:148)
midje.parsing.1_to_explicit_form.facts$expand_fact_body.invoke(facts.clj:140)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invokeStatic(facts.clj:186)
midje.parsing.1_to_explicit_form.facts$complete_fact_transformation.invoke(facts.clj:182)
midje.sweet$fact$fn__7642.invoke(sweet.clj:192)
midje.parsing.util.error_handling$parse_and_catch_failure.invokeStatic(error_handling.clj:41)
midje.parsing.util.error_handling$parse_and_catch_failure.invoke(error_handling.clj:17)
midje.sweet$fact.invokeStatic(sweet.clj:184)
midje.sweet$fact.doInvoke(sweet.clj:170)
midje.repl$load_facts$fn__9593.invoke(repl.clj:208)
midje.repl$load_facts.invokeStatic(repl.clj:194)
midje.repl$load_facts.doInvoke(repl.clj:194)

Midje seems to have difficulty handling argument expressions in provided entries.
To get around this you can convert the expressions into values by let-bindind them:

(fact "some fact"
          (let [publisher-name (:name (:exchange publisher-desc))
                publisher-type (:type (:exchange publisher-desc))]
            (setup-publisher publisher-desc) => anything
            (provided
              (lanex/declare anything publisher-name publisher-type anything)  => anything :times 1
              (lancore/close anything) => anything :times 2)))
clov0 commented

Thanks

While the workaround I mentioned should be fine, I just realized that what you are trying to do should be supported by Midje. For example, this test demonstrates that you can use expressions as arguments in functions mocked by provided.

Here is a PR that I think handles this issue: #393

The original idea was to provide a shorthand syntax for this:

(provided
    (inner 1) => ..inner-1-result...
    (outer ..inner-1-result..) => 5

... which would be:

(provided
  (outer (inner 1)) => 5

I always felt uneasy about that. It was occasionally useful but also seemed to encourage the tests to be about implementation rather than about the logical structure of the problem/solution domain. (Diagram at the end of https://github.com/marick/Midje/wiki/The-idea-behind-top-down-development)

Using keywords as arguments pushes it further in the implementation direction, in the sense that we're now embedding structural knowledge in the test. Which seems iffy. But, on the other hand, hiding structure with, for example:

(def x :x)

... is not idiomatic at all.

In all the time since Clojure 1.2, I still haven't figured out how to think about encapsulation.

Probably best to give people more options.