Why can't with-redefs be applied around a deftest? - clojure

The with-redefs function appears to be just what is needed to mock/stub out dependencies in clojure tests. I'm using clojure.test [1.10.1]
Initially it gave me a lot of grief, where the new bindings were not applied when I ran the test. Finally I got the following setup to work as expected. The dependencies are required from other namespaces
(ns abc
(:require [anotherns.id-gen-mock :as mock])
(deftest test-buy
(testing "Appends trade to the trades log"
(let [mock (atom {})]
(with-redefs [id-gen/get-next-id mock/get-next-id
save-trade (fn [t] (reset! mock {:trade t}))]
... test code
))))
Now I realized, that the mocks can be common to all my tests, so I moved it up like so.
(with-redefs [id-gen/get-next-id mock/get-next-id
save-trade identity]
(deftest test-holdings
(testing "after 1 buy"
... test code
Now the new bindings are not used, the real dependencies are invoked - failing the test.
I see some posts on SO mentioning something about "direct linking" but I couldn't really fathom why it works in Case1 but not in Case2. If I move with-redefs back under the deftest form, it works again.

According to the docstring (https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/with-redefs), with-redefs restores the original value after executing the body. In the given case, the body defines a test. With-redefs thus governs the tests' definition, but not their execution. When the tests execute, any reference to get-next-id resolves that symbol to its current value, which by that time will be the real one, not the mock. (This follows logically: if case No.1 holds, and the docstring holds, then case No.2 could not hold.)
For reasons already mentioned in comments, with-redefs is not usually a tool of first choice. Clojure offers more robust techniques, e.g., make higher-order subsystems and use fixtures to configure them for testing.

Related

Group multiple namespace definitions under one namespace

Suppose I have the namespaces foo.car.components.engine, foo.car.components.transmission, foo.car.components.brakes.
In foo.car.components.engine there is (defn engine [] ...), foo.car.components.transmission there is (defn transmission [] ...),
foo.car.components.brakes there is (defn brakes [] ...).
I'd like to make these available in foo.car.components such that other namespaces only need to require foo.car.components to use engine, transmission, and brakes.
The following works, but I'm wondering if there are cleaner ways to do this or if it's even good practice.
(ns foo.car.components
(:require
[foo.car.components.engine :as engine]
[foo.car.components.transmission :as transmission]
[foo.car.components.brakes :as brakes]))
(def engine engine/engine)
(def transmission transmission/transmission)
(def brakes brakes/brakes)
I don't offhand know of a better way, but this way does come with downsides, so take these into consideration when deciding if you want to use this:
It doesn't transfer Meta information to the "wrapper", so any docstrings/other information attached to the main function won't show up in IDEs when you use the wrapper.
Along the same vein, because the wrapper doesn't have an argument list, if you ctrl+q the wrapper functions, it also won't show the available argument lists of the main function.
Having said that, Seesaw, a major Clojure library that wraps Swing does use this "technique". If I ever forget the docs/arguments of a function that has a "convenience wrapper", I just have to hit ctrl+b twice (in IntelliJ), and it will take me to the original source where I can look it over. It's ironically inconvenient, but I guess that's the price for convenience elsewhere.
To get around these faults, you could write a function (or a macro that wraps def) that transfers Meta information. Considering argument list information is stored as Meta information, that might be enough to overcome the faults.
This answer doesn't really answer your question, so I hope someone else is able to give some insight here. I thought that this was relevant information though.
You can use:
import-vars
from https://github.com/ztellman/potemkin
It doesn't seem that this feature is provided by Clojure API. You can consider https://github.com/ptaoussanis/encore look for defalias.
On the other hand, if you have brakes, transmission and engine as public interfaces and you can use them separately why would you merge them? As opposite, you can provide all definitions in components or even car which will use in turn brakes, transmission and engine. In such way, it is not necessary to expose all components.
I think the way you're doing it is the best way, because it makes explicit the sources of the defs in each namespace. If you have large namespaces with a lot of functions, you could write a basic helper function to do this for you:
(ns foo.utils)
(defn export-refs
[target-ns source-namespaces]
(doseq [ns source-namespaces
[sym f] (ns-interns ns)
:let [existing (get (ns-interns target-ns) sym)]]
(when (and existing (not= (var-get existing) f))
(throw (Exception.
(format (str "Cannot refer to symbol %s in %s from %s, because that symbol "
"already exists in the target namespace")
sym (ns-name ns) (ns-name target-ns)))))
(intern target-ns sym f)))
(ns foo.car.components.engine)
(defn engine [] (println "engine"))
(ns foo.car.components.transmission)
(defn transmission [] (println "transmission"))
(ns foo.car.components.brakes)
(defn brakes [] (println "brakes"))
(ns foo.car.components
(:require [foo.utils :refer [export-refs]]))
(export-refs 'foo.car.components '[foo.car.components.engine
foo.car.components.transmission
foo.car.components.brakes])
(ns user
(:require [foo.car.components :refer [engine transmission brakes]]))
(engine) ;; Prints "engine"
(transmission) ;; Prints "transmission"
(brakes) ;; Prints "brakes"
Whether it's "good practice" or not is up to you. Obviously it has the advantage of splitting code into smaller files with specific functionality, while allowing the import of only a single namespace. The disadvantage is that there's a bit of indirection in where the functions come from, which will make finding the function sources more difficult, and there's more risk of name collisions.

How to define prerequisite functions in Midje?

I should start by saying I'm new to Clojure, and FP in general. I've been reading the documentation about how to define prerequisites in Midje, but I can't make sense of some of it.
My understanding was that to do top-down TDD, you're supposed to start by writing a test in your test module, with an unfinished statement at the top that 'declares' all the prerequisite functions you haven't defined yet. Then you can fiddle with those prerequisite functions in the provided function within your test (describing their return values and such).
My confusion lies in how you're supposed to get your actual source module to recognize the prerequisite functions. Here's a very simple and contrived example I'll use to illustrate my meaning:
;;; in my run_game_test module
(ns clojure-ttt.run-game-test
(:require [midje.sweet :refer :all]
[clojure-ttt.run-game :refer [start-game]]))
(unfinished do-turns)
(fact "`start-game` returns whatever `do-turns` returns"
(start-game) => ..winner..
(provided
(do-turns) => ..winner..))
And then to make the test fail properly, I just write a stub of a function in my run_game module.
(ns clojure-ttt.run-game)
(defn start-game
[])
So far so good. I run the tests and they fail because:
a) do-turns isn't getting called
b) start-game isn't returning anything.
So now to make the test pass, by changing start-game to call and return (do-turns). For the record, do-turns is a hypothetical prerequisite function that I'm going to get from a not-yet-existing module--which, as I understand, is how top-down TDD works.
(defn start-game
[]
(do-turns))
Now, understandably, I get a huge error; Clojure can't resolve the symbol do-turns. So I thought, maybe if I (declare do-turns) at the top I could keep it from blowing up. No, I get a different error because I'm trying to call an unbound function.
I tried a couple other ways of getting Clojure to recognize do-turns but it seems like the unfinished statement is giving it issues. Am I just using unfinished wrong?
From the Midje docs:
unfinished is similar to Clojure's declare in that it can take multiple arguments and define a var for each one of them. Unlike declare, it binds the var to a function that blows up if called
So when you do (unfinished do-turns) and then call it with (do-turns) an exception is thrown:
Error #'do-turns [the var] has no implementation, but it was called like this:
(do-turns) midje.util.exceptions/user-error (exceptions.clj:13)
You can fix this by removing the (unfinished do-turns) part and provide an implementation of do-turns. E.g.,
(ns clojure-ttt.run-game)
(defn do-turns)
[])
(defn start-game
[]
(do-turns))
and refer it in your test
(ns clojure-ttt.run-game-test
(:require [midje.sweet :refer :all]
[clojure-ttt.run-game :refer [start-game do-turns]])) ; <- do-turns
;; Remove `(unfinished do-turns)` since it is no longer unfinished
(fact "`start-game` returns whatever `do-turns` returns"
(start-game) => ..winner..
(provided
(do-turns) => ..winner..))
;; => returns true
Note that when you have provided an implementation of do-turns, (unfinished do-turns) will throw an exception since an implementation already exists, so it is removed.
Now you have shown that a call to (start-game) returns what (do-turns) returns.

Clojure macros weirdness when running jars

Below is a simple Clojure app example created with lein new mw:
(ns mw.core
(:gen-class))
(def fs (atom {}))
(defmacro op []
(swap! fs assoc :macro-f "somevalue"))
(op)
(defn -main [& args]
(println #fs))
and in project.clj I have
:profiles {:uberjar {:aot [mw.core]}}
:main mw.core
When run in REPL, evaluating #fs returns {:macro-f somevalue}. But, running an uberjar yields {}. If I change op definition to defn instead of defmacro, then fs has again proper content when run from an uberjar. Why is that?
I vaguely realize that this has something to do with AOT compilation and the fact that macro-expansion occurs before the compilation phase, but clearly my understanding of these things is lacking.
I ran into this issue while trying to deploy an application that uses a very nice mixfix library, in which mixfix operators are defined using a global atom. It took me quite a long time to isolate the issue to the example presented above.
Any help will be greatly appreciated.
Thanks!
The real problem here is that your macro is incorrect. You forgot to add backquote character:
(defmacro op []
`(swap! fs assoc :macro-f "somevalue"))
; ^ syntax-quote ("backquote")
This operation is called syntax-quote and it's very important here, because macros in clojure modify your code during its compilation.
So, as a result you've got an impure macro, modifying fs atom whenever your code is compiled.
Since your macro doesn't produce any code, (op) call in your example does nothing at all (only it's compilation do). It appear to be working in REPL because compilation and execution is handled by the same clojure instance (see Timur's answer for details).
This is indeed related to the AOT, and the fact that some side effects are expected when a top-level code is executed - here at macro expansion time. The difference between the lein repl (or lein run) and the uberjar is in when exactly this happens.
When lein repl is executed, REPL starts and then loads the mw.core namespace automatically, if it is defined in project.clj, or one does it manually. When namespace is loaded, first the atom is defined, then macro is expanded and this expansion changes the value of the atom. All this happens in same runtime environment (in REPL process), and after the module is loaded, atom has an updated value in this REPL. Executing lein run will do pretty much the same - load namespace and then execute -main function in the same process.
And when lein uberjar is executed - same thing happens and this is the problem now. Compiler, in order to compile the clj file will first load it and evaluate the top level (I learned it myself from this SO answer). So the module is loaded, top level is evaluated, macro is expanded, reference value is changed and then, after compilation completes, the compiler process, the one where reference value just changed, ends. Now, when the uberjar is executed with java -jar this spawns the new process, with a compiled code, where the macro is already expended (so (op) is already "replaced" with the code the op macro generated, which is none in this case). Therefore, atom value is unchanged.
In my opinion, good fix would be to not rely on side effects in a macro.
If stick to the macro anyway, the way to make this idea work is to skip the AOT for the module where macro expansion happens and load it lazily from the main module (again, same solution as in the other SO answer I mentioned). For example:
project.clj:
; ...
:profiles {:uberjar {:aot [mw.main]}}) ; note, no `mw.core` here
; ...
main.clj:
(ns mw.main
(:gen-class))
(defn get-fs []
(require 'mw.core)
#(resolve 'mw.core/fs))
(defn -main [& args]
(println #(get-fs)))
core.clj:
(ns mw.core
(:gen-class))
(def fs (atom {}))
(defmacro op []
(swap! fs assoc :macro-f "somevalue"))
(op)
I'm not sure myself, however, if this solution is stable enough and that there are no edge cases. It does work though on this simple example.

How is 'provided' implemented in a fact in Midje?

I was reading Clojure in Action chapter 8 about TDD and experimented with the stubbing macro. It uses the dynamic binding mechanism to stub functions. Alas, in Clojure 1.3 it is not possible to use the binding mechanism for non-dynamic vars, so the stubbing macro doesn't work in most cases, unless you explicitly declare the var which points to a function dynamic. Then I wondered how stubbing is done in Midje and tried to find the source for 'provided', but I couldn't find it. So here it goes:
How is 'provided' implemented in a fact in Midje? Can someone explain this in detail?
Clojure 1.3 provides a with-redefs macro that works even with vars that haven't been declared dynamic:
user=> (def this-is-not-dynamic)
user=> (with-redefs [this-is-not-dynamic 900] this-is-not-dynamic)
900
For backward compatibility, Midje uses its own version, whose guts look like this:
(defn alter-one-root [[variable new-value]]
(if (bound? variable)
(let [old-value (deref variable)]
(alter-var-root variable (fn [current-value] new-value))
[variable old-value])
(do
(.bindRoot variable new-value)
[variable unbound-marker])))

How do I expect failure in a unit test?

We're writing unit tests for our code in Clojure using clojure.test.
Some of our tests ignore the API and purposely break the code, in order to serve as documentation for latent deficiencies in the code.
However, we want to distinguish between failures of these tests, and failures of normal tests.
We haven't seen any suggestions in the clojure.test documentation -- only (is (thrown? ...)), which of course doesn't do what we need.
Any suggestions? Basically, we're looking for something like (is (not <condition>)), except that the test framework should record an expected failure -- something like this.
I have made tests throw an exception when they 'fail' like this, and then used thrown? to test if the exception arrives as expected. There very well may exist a more elegant way, but this gets the job done.
As #andy said you can rebind report function.
(defmacro should-fail [body]
`(let [report-type# (atom nil)]
(binding [clojure.test/report #(reset! report-type# (:type %))]
~body)
(testing "should fail"
(is (= #report-type# :fail )))))
And use this macro like this:
(should-fail (is (= 1 2)))
That will be successful passing test.
Rebind the report function as documented in clojure.test. From there you can change how the 'failure' is handled and reported on.
A little late to the party, I know, but if you're using Leiningen you could use test selectors to exclude expected failures from the "normal" build. In your project.clj:
:test-selectors {:default #(not :expected-failure %)
:expected-failure :expected-failure}
Then write your test as:
(deftest ^:expected-failure test-with-expected-failure ...)
Calling lein test will run only those tests without the :expected-failure metadata. Calling lein test :expected-failure will run your expected failure tests. Maybe not quite what you were hoping for, but IMHO better than having to use thrown? all over the place. At least this way expected failures are explicitly documented in the code and won't interfere with the build.