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.
Related
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.
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.
Imagine there is the following require-statement inside a namespace:
(ns my.core
(:require '[mylib.sth :as thing]))
(def somevar 123)
Is there a way to access mylib.sth via thing also from outside this namespace? I mean to somehow get the same behavior as for the definition somevar:
(ns somethingelse)
my.core/somevar
;; =123
(my.core/thing/myf "param") ;; something like this
;; ...
resolve and ns-resolve were made for this situation.
They will return nil if the symbol is not found, otherwise they return the var, which you can deref in order to get the current bound value.
user=> (ns my.test)
nil
my.test=> (def hidden 5)
#'my.test/hidden
my.core=> (ns my.core (:require [my.test :as t]))
nil
my.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x25930632 "user"]
user=> #(resolve 'my.test/hidden)
5
user=> #(ns-resolve 'my.core 't/hidden)
5
This works, but it's also a last resort. It should be reserved for situations where you are writing code that uses namespaces and bindings that you expect to find at run time that cannot be accessible at compile time. For example I use resolve to avoid transitive AOT of my project while compiling a stub that is callable from Java; the stub -main invokes require and then resolve at runtime, using the resolved values to access the real code.
If all you are looking for is a convenience or syntactic shortcut, the better option is to explicitly require a namespace if you want to use its values.
I have this macro:
(defmacro widget [msg-type value & app-key]
`(defrecord ~msg-type [~value]
Message
(~'process-message [msg# app#]
(let [state# (~#app-key app#)]
(dissoc
(->>
(merge state# msg#)
(assoc app# ~#app-key))
:errors)))))
Message is a protocol defined in a clojurescript dependency, with a process-message function.
When I try to use widget like so
(ns my.cljs.ns
(:require-macros [my.ns.macros :as macro])
(:require [petrol.core :refer [Message]]))
(macro/widget A-Record a-field :a-key)
I get this error:
Bad method signature in protocol implementation,
my.ns.macros/Message does not declare method called
process-message ...
How can I get Message to refer to petrol/Message instead of my.ns.macros/Message?
You need the power of the mystical ~' operator :)
I see you already invoked it for process-message, so perhaps you are already acquainted with why; but for the purposes of the answer, stuff in the backtick gets fully namespace qualified, where as evaluate quote puts the literal symbol in place.
(macroexpand-1 '(widget :a :b))
And the error message indicate that you need to ~'Message if you want to avoid it having the current ns attached to it.
However fully qualifying Message with the petrol namespace would be a good move IMO
petrol.core/Message
That way you don't need to rely on it being referred in the ns declaration. Note you don't need to ~' it either.
Also I would be wary of (~#app-key app#) because app-key are optional... you could get nothing passed in which would call whatever #app is, which doesn't sound like something you want to happen. Similarly passing more than one seems wierd to. Maybe it should be a required param?
Other is a more complex version (wrapping) of What. It does what What does but much more. I took care to define 2 namespaces.
(ns what)
(defprotocol IWhatever
(whatever [this]))
(deftype What []
IWhatever
(whatever [this]
(str "whatever")))
(whatever (->What))
(ns other (:require what))
(deftype Other []
what/IWhatever
(whatever [this]
(what/whatever (what/->What))))
(whatever (->Other)) ;bad line
The error is:
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to resolve symbol: whatever in this context, compiling:(C:\...)
Why won't this last expression resolve? It's like the name can't be found, but as you can see I redefined it under the current namespace.
It's nonsense code but I used the simplest example of the problem to illustrate the point. I'm running this in LightTable if that's relevant.
Qualify the final line with the namespace of where the protocol was defined. I kept thinking to try to call the whatever method in the other namespace since it was defined there.
(what/whatever (->Other))
Thanks goes to #soulcheck and everyone else who took time to help.