Is there a clean way of adding spec generators in test code that apply to a spec defined in another source file? For example, say I have the following under my main src/ directory:
(s/fdef my-func
:args (s/cat
:statement (partial instance? java.sql.PreparedStatement))
:ret bool?)
In order to do generative testing I will need a generator for the statement argument, say one that generates mock statements, but I'd rather not mix test code with production nor make other projects that use this library also transitively pull down the test libraries.
So what I'm looking for is a convention to apply generators atop an existing spec without having to modify the source for that spec.
clojure.spec.test.alpha/check takes an optional map of generator overrides:
https://clojure.github.io/spec.alpha/clojure.spec.test.alpha-api.html#clojure.spec.test.alpha/check
:gen map from spec names to generator overrides
Note that you need to use a named spec, so in your example above you'd need to s/def something like ::statement:
(s/def ::statement (partial instance? java.sql.PreparedStatement))
Then your function spec :args would look like (s/cat :statement ::statement).
You could then supply a generator to check like this in your test project:
(st/check sym-or-syms {:gen {::statement my-test-proj-generator}})
a convention to apply generators atop an existing spec without having to modify the source for that spec
I think with-gen is appropriate for this: it takes an existing spec and returns that spec with a given generator.
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.
I am trying to create a DSL for packaging software using clojure. I want to define macros, functions, etc. and then load a file, and I want all of those things I define to be in the same namespace as the file itself, so that if I define a macro abe, I can use it in the file without using abe's fully qualified name.
I have this so far:
(binding [*ns* (find-ns 'my-namespace)]
(load-file "Myfile"))
But I get the feeling I'm doing it wrong.
Also, it would be nice to even have locally bound variables accessible within the clojure file. Something like this:
(let [a 1]
(binding [*ns* (find-ns 'my-namespace)]
(load-file "myfile")))
Where a is usable from within myfile.
The idea is to allow a programmer to specify how to package something in real clojure, but with the language extended in ways that are transparent to the programmer.
Is there a better way to do this? Is there some better "idiomatic" way of creating a DSL in clojure?
I apologize if the question is trivial, but some googling is not leading me anywhere. What is the general syntax of defmulti and defmethod? I can write simple multimethods, but I am not sure where I can put the docstring, pre and post conditions, metadata and so on.
I am actually interested in ClojureScript more than in Clojure, so if there are differences between the two, please tell me.
In a REPL you can use the doc function to get the function arguments and (most of the time) an explanation of the options. As for ClojureScript, these two functions are macros, which means they are expanded at compile time and should behave exactly as they do in regular Clojure. That is, as long as ClojureScript can handle the code the macro generates.
user=> (doc defmulti)
-------------------------
clojure.core/defmulti
([name docstring? attr-map? dispatch-fn & options])
Macro
Creates a new multimethod with the associated dispatch function.
The docstring and attribute-map are optional.
Options are key-value pairs and may be one of:
:default the default dispatch value, defaults to :default
:hierarchy the isa? hierarchy to use for dispatching
defaults to the global hierarchy
nil
user=> (doc defmethod)
-------------------------
clojure.core/defmethod
([multifn dispatch-val & fn-tail])
Macro
Creates and installs a new method of multimethod associated with dispatch-value.
nil
At Clojuredocs: defmulti, defmethod.
If you don't find the examples there detailed enough, you might consider adding your own (once you've gotten all your questions answered).
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])))
Is there an idiomatic way to get available namespaces that can be used?
(all-ns) returns only already used namespaces. (Package/getPackages) returns all Java packages available for import, but only those Clojure namespaces that are already used.
Then I stumbled upon this post, but it uses some classpath magic.
So I want to get something like ('clojure.core 'clojure.set ... 'clojure.contrib.accumulators 'clojure.contrib.condition ...) if I have the clojure.jar and contrib.jar on my classpath, but I haven't used anything yet.
You will need to do "classpath magic". Since there is no kind of registry, you have to walk the classpath and look in every clojure source file to determine what namespaces are available. (In case the files are not AOT compiled. Otherwise you'll need a different heuristic.)
I think the function used in the linked post is the best way to go: clojure.contrib.find-namespaces/find-namespaces-on-classpath.
Deprecated since Clojure 1.3.0; use now clojure.tools.namespace.find/find-namespaces and clojure.java.classpath/classpath from http://github.com/clojure/java.classpath
I have found bultitude to be a great tool for doing this.
Example:
user=> (require '[bultitude.core :as b])
nil
user=> (take 10 (b/namespaces-on-classpath))
(bultitude.core-test bultitude.core clojure.data clojure.string clojure.test clojure.xml clojure.inspector clojure.repl clojure.set clojure.test.junit)
user=> (b/namespaces-on-classpath :prefix "bultitude")
(bultitude.core-test bultitude.core)