I am having a hard time creating a simple unit test in Clojure using with-redefs for mocking on Windows. The test code, the function I'm about to test and the function to be mocked are all in different namespaces/files:
Function under test:
(ns com.mynamespace.a
(:require [com.mynamespace.b :as b] ) )
(defn afunc [param]
(b/bfunc param))
Dependent b namespace to be mocked out:
(ns com.mynamespace.b)
(defn bfunc [param]
; External call
)
Test code
(ns com.mynamespace.a-test
(:require [com.mynamespace.a :as a]
[com.mynamespace.b :as b] )
(:use [clojure.test]))
(deftest a-tests
(with-redefs [b/bfunc (constantly "dummy")]
(print (a/afunc "test"))
)
)
I think I use with-redefs correctly, because my unit test runs fine on Linux machines (docker container or virtual Ubuntu), but it just does not work when executing/developing them on my Windows dev machine: the with-redefs bindings are not applied and the test wants to make real e.g. http calls, which I'd like to mock. It is the same case if I run lein test, or try executing the test from a REPL through Eclipse CCW.
I know with-redefs has some strange behaviour, but as my test works well on Linux, I guess I miss something, when running this project's test on Windows. I would like to achieve fast feedback loops, when writing tests on my Windows machine.
Do you have any idea what I'm doing wrong?
Thanks,
Andras
Turned out that this issue was specific to a project I'm working on, because of the -Dclojure.compiler.direct-linking=true jvm-opts flag.
After turning off direct linking, my tests work on my Windows dev machine as well. I wonder why they passed on Linux though, but perhaps it was not applied, when running lein test there.
Related
Here is a small part about debugging in kaocha: https://cljdoc.org/d/lambdaisland/kaocha/1.69.1069/doc/8-plugins, which i do not understand.
Is there a way to debug kaocha tests or at least a way to execute a test directly from the REPL, so that i can debug the test?
Don't know about kaocha, but deftest macro just generates ordinary function by the test name passed to it in the test namespace:
(require '[clojure.test :as t])
(require '[clojure.walk :as w])
(w/macroexpand-all '(t/deftest a
(println "before test")
(t/is 1 1)
(println "after test")))
;;=> (def a (fn* ([] (clojure.test/test-var (var a)))))
so, you just call it from anywhere you want, just importing the test namespace:
user=> (ns my-tests)
nil
my-tests=> (require '[clojure.test :as t])
nil
my-tests=> (t/deftest some-test (let [x 101] (println "x is " x) (t/is x 101)))
#'my-tests/some-test
my-tests=> (in-ns 'user)
#object[clojure.lang.Namespace 0x4c98a6d5 "user"]
user=> (my-tests/some-test)
;;=> x is 101
nil
Not a direct answer, but I tried Kaocha & didn't like it as much as the original Leiningen driven tests, especially with the lein-test-refresh plugin. Besides running from the REPL as in the other answer, you can also run either all tests or only one test from the command line.
With the Test Refresh Plugin, the computer will watch your your source for any changes. Upon any editor save, the code will be reloaded & recompiled, then all modified tests will be re-run. The tests are typically completed before your finger has left the <enter> key.
You can see an example of using lein-test-refresh here. Below is an excerpt from the README:
Running Unit Tests with lein test-refresh
IMHO, the lein-test-refresh project provides the best way of doing iterative development in Clojure, re-running unit tests every time you save a source file from the editor.
> lein test-refresh
or make an alias:
alias lctr="lein do clean, test-refresh" # lctr => Lein Clean Test-Refresh
with result:
Testing _bootstrap
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
result => "Hello, World!\n"
Ran 4 tests containing 6 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 07:56:48.252 (run time: 6.709s)
in this instance, the test output pauses after printing the elapsed time, and waits to re-run upon any file change. For example, make a simple change in the namespace demo.core by adding the word "Again!" to the println expression, and watch as the tests are automatically re-run:
...
Testing tst.demo.core
result => "Hello, World! Again!\n"
...
Finished at 07:59:53.863 (run time: 0.034s)
Note that the tests were all re-run in only 34 milliseconds (nearly instantaneous), since the JVM was already started and Clojure itself (plus library code) has already been compiled.
Let's say I create a new Leiningen project (lein new app example) and add some code in example/src/example/core.clj that makes use of :gen-class:
(ns example.core
(:gen-class :extends javafx.application.Application))
(defn -start [this stage]
(.show stage))
(defn -main [& args]
(javafx.application.Application/launch example.core args))
If I then create a JAR (lein uberjar) and run it, everything works fine. However, if I instead try to run my app directly (lein run), I get a ClassNotFoundException. In addition, if I open a REPL (lein repl), I first get the same error as before, but after running this code:
(compile 'example.core)
the error no longer appears in lein run or lein repl.
Could someone please explain to me what exactly is going on here, and how I can run my app directly without needing to manually compile my code from a REPL?
Edit: After fooling around a bit more, I found that the solution to this problem is to add
:aot [example.core]
to project.clj. (Thanks #Mars!) I'm still confused, though, because I had previously tried simply removing ^:skip-aot, which (according to the docs) should work:
This will be AOT compiled by default; to disable this, attach
^:skip-aot metadata to the namespace symbol.
But it doesn't. Why?
Another edit (if I should split any of this into a separate question, let me know and I'll do so): I've been playing with hyphens (lein new app my-example), and weird stuff has been happening. This doesn't work:
(ns my-example.core
(:gen-class :extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.core args))
But this does:
(ns my-example.core
(:gen-class
:name my-example.Core
:extends javafx.application.Application))
;; ...
(defn -main [& args]
(javafx.application.Application/launch my-example.Core args))
So my class name can either start with a lowercase letter (example.core) or contain a hyphen (my-example.Core), but not both? I really don't get it.
And finally, lein uberjar fails on that final example (with the explicit :name), because
Warning: The Main-Class specified does not exist within the jar. It may not be executable as expected. A gen-class directive may be missing in the namespace which contains the main method.
As far as I can tell, the only way to fix that is to split the Application subclass into a separate namespace. Is there another way?
Agreed with #Mars, the problem is that lein run does not AOT the example.core namespace. The default Leiningen template made the example.core non AOT:
(defproject example "0.1.0-SNAPSHOT"
...
:main ^:skip-aot example.core
...)
My best guess is that you could define your app using defrecord and use that as a class instead of the namespace.
JavaFX 8, Java 1.8.0_31, Windows 7 x64
I have a minimal JavaFX program in Clojure. The (ns...) clause is able to import the required Java packages fine except the classes in javafx.scene.control, such as Button and TextField, etc.
I have to put the import for these after initializing the toolkit. Why can't I import these classes before the toolkit is initialized? I'm not actually creating any objects yet... so I'm guessing JFX is somehow doing something in the background while these classes are imported, requiring the initialization first. Below is my complete lein project (minimized from the actual application where I saw this problem, and without all the nice macros that clean up the JFX syntax):
File project.clj:
(defproject jfx-so "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.6.0"]]
:main jfx-so.core)
File src/jfx_so/core.clj:
(ns jfx-so.core
(:import [javafx.scene Scene]
[javafx.scene.layout BorderPane]
[javafx.stage Stage]))
(defonce force-toolkit-init (javafx.embed.swing.JFXPanel.))
;; For some reason the following must be imported after initting the toolkit
(import [javafx.scene.control Button])
(defn -main [& args]
(javafx.application.Platform/runLater
#(doto (Stage.)
(.setScene (Scene. (BorderPane. (Button. "Hello"))))
(.show))))
Thanks! :)
I haven't had a problem with this. Perhaps it has to do with your defonce?
I do my imports first. But I do make sure to init the FX-envoronment before instanciating any FX-classes. So after your -main-method I would put:
(defn -main [& args]
;;body here
)
;; initialze the environement
(javafx.embed.swing.JFXPanel.)
;; ensure I can keep reloading and running without restarting JVM every time
(javafx.application.Platform/setImplicitExit false)
;; then
(-main)
Hope this helps.
the :injections keyword is really useful. However, I am hoping to dynamically install a couple of functions in core for debugging purposes. How can this be done?
:injections [(require 'spyscope.core)
(use '[cemerick.pomegranate :only (add-dependencies)])
(use '[clojure.tools.namespace.repl :only (refresh)])]
Ideally, I would want refresh to stay around to that I can use it everywhere
You could use intern for this purpose, although I suspect there might be a better way to have debugging functions available all the time. I've used intern with clojure.core the times that I wanted to mess around with existing functions to learn stuff, but injecting functions in other namespaces feels too hackish.
(intern 'clojure.core 'refresh (fn [] (println "refreshed!"))
(ns another-ns)
(refresh)
;=> refreshed!
And in your project.clj you can use the :repl-options key, specifically :init. This, though, depends on the workflow you have in mind, since the function will not be available in all the namespaces that already exist when the REPL fires up, because they all have already refered the public vars in clojure.core.
You could however, call (clojure.tools.namespace.repl/refresh) once, when the REPL starts, to get all namespaces reloaded and then the function should be available from then on. I just tried the following and it seems to work:
:repl-options {:init (do (require 'clojure.tools.namespace.repl)
(intern 'clojure.core 'refresh clojure.tools.namespace.repl/refresh)
(clojure.tools.namespace.repl/refresh))}
In my Clojure codebase, I have defined several protocols and several defrecords. I am using clojure.test to unit test the concrete functions defined within my defrecords.
For example, let's say I have the following source files:
In src/foo/protocols.clj:
(ns foo.protocols)
(defprotocol RequestAcceptability
(desired-accepted-charset [this]))
In src/foo/types.clj:
(ns foo.types
(:use [foo.protocols :only [RequestAcceptability desired-accepted-charset]])
(defrecord RequestProcessor
[field-1 field-2]
RequestAcceptability
(desired-accepted-charset [this]
...implementation here...))
In test/foo/types_test.clj:
(ns foo.types-test
(:use [clojure.test])
(:use [foo.protocols :only [RequestAcceptability desired-accepted-charset]])
(:import [foo.types RequestProcessor]))
(deftest test-desired-accepted-charset_1
...test code here...)
I am using Clojure 1.4, Leiningen 2, nrepl within Emacs.
The annoyance I am facing is that when I go to run my unit tests (e.g., using C-c C-, sequence), I get a ClassNotFoundException: foo.types.RequestProcessor. To get around this, I'm doing the manual work of evaluating, individually, each of my protocol and defrecord forms. I.e., I'll navigate over to my protocols.clj and evaluate (C-M-x key sequence for nrepl) my defprotocol form; and then I'll navigate to my types.clj and evaluate my defrecord form; and then finally I'm able to successfully run my unit tests w/out getting the ClassNotFoundException.
Of course, in my real code base, I have to do this for all my protocols and defrecords, and so it is very tedious and time consuming. Also, if I simply drop to a shell and do lein test, I get the same ClassNotFoundException.
Is there a better way?
Thank you for your time and help.
You need to require the namespace that contains the defrecord in your test namespace.
(:import [foo.types RequestProcessor])) won't work on its own, since that only works for Java classes that already exist on the classpath. This isn't the case here, since you are using defrecord to dynamically create the class at runtime.
import is usually only needed for Java interop.
defrecord dynamically generates the class when it is run. Once you go into the namespace and run the code, then the class comes into being and the tests load normally because the class now exists. If you use the namespace from within your tests in addition to importing the class does it load properly for you?