I'm trying out Clojure web development with Ring + Compojure + lib-noir, and I can't figure out how to test the session state.
I think you can just mimic the style used for the actual session tests in ring: https://github.com/ring-clojure/ring/blob/master/ring-core/test/ring/middleware/session/test/cookie.clj.
Just create a cookie-store, and then read/write to it, and assert the handlers respond accordingly. Given you are also using lib-noir, perhaps this example is more appropriate: https://github.com/noir-clojure/lib-noir/blob/master/test/noir/session_test.clj.
If you mean for unit tests, you can use binding, which will create new bindings for the vars.
You can check a good explanation that can be found here.
A sample unit tests with lib-noir
(ns your.test.core
(:use [clojure.test])
(:require [noir.session :as s]))
(binding [s/*noir-session* (atom {})]
; store new sessions
(s/put! "xxxx" {:value "1234"})
(s/put! "my_session" {:value "abcdefg"})
; run tests
(is (= {:value "1234"} (s/get "xxxx")))
(is (= {:value "abcdefg"} (s/get "my_session"))))
You can check the source code of noir.session here.
Related
I'd like to mock out a macro in a namespace.
For instance, clojure.tools.logging/error.
I tried with-redefs with no luck
(def logged false)
(defmacro testerror
{:arglists '([message & more] [throwable message & more])}
[& args]
`(def logged true))
(deftest foo
...
(with-redefs
[log/error testerror]
...
That gave this error:
CompilerException java.lang.RuntimeException: Can't take value of a macro
Amalloy provided you the answer for your direct question on how to mock a macro - you cannot.
However, you can solve your problem with other solutions (simpler than moving your whole application to component dependency injection). Let me suggest two alternative implementations (unfortunately, not very straightforward but still simpler than using component).
Mock the function called by logging macro
You cannot mock a macro but you can mock a function that will be used when the logging macro get expanded.
(require '[clojure.tools.logging :as log])
(require '[clojure.pprint :refer [pprint]])
(pprint (macroexpand `(log/error (Exception. "Boom") "There was a failure")))
Gives:
(let*
[logger__739__auto__
(clojure.tools.logging.impl/get-logger
clojure.tools.logging/*logger-factory*
#object[clojure.lang.Namespace 0x2c50fafc "boot.user"])]
(if
(clojure.tools.logging.impl/enabled? logger__739__auto__ :error)
(clojure.core/let
[x__740__auto__ (java.lang.Exception. "Boom")]
(if
(clojure.core/instance? java.lang.Throwable x__740__auto__)
(clojure.tools.logging/log*
logger__739__auto__
:error
x__740__auto__
(clojure.core/print-str "There was a failure"))
(clojure.tools.logging/log*
logger__739__auto__
:error
nil
(clojure.core/print-str x__740__auto__ "There was a failure"))))))
As you can see, the function that does actual logging (if a given level is enabled) is done with clojure.tools.logging/log* function.
We can mock it and write our test:
(require '[clojure.test :refer :all])
(def log-messages (atom []))
(defn log*-mock [logger level throwable message]
(swap! log-messages conj {:logger logger :level level :throwable throwable :message message}))
(with-redefs [clojure.tools.logging/log* log*-mock]
(let [ex (Exception. "Boom")]
(log/error ex "There was a failure")
(let [logged (first #log-messages)]
(is (= :error (:level logged)))
(is (= "There was a failure!" (:message logged)))
(is (= ex (:throwable logged))))))
Use your logging library API to collect and inspect log messages
Your logging library API might provide features that would allow you to plug into in your test to collect and assert logging events. For example with java.util.logging you can write your own implementation of Handler that would collect all logged log records and add it to a specific (or root) logger.
You cannot do this. The point of macros is that they are expanded when the code is compiled, and after that they are gone. The original code that included a call to the macro is unrecoverable. You cannot retroactively redefine a macro at runtime: you're too late already.
An alternative approach, if you want to have swappable logging implementations, would be to use something like Component for dependency injection, and use a different logging component depending on whether you are running tests or running your real program. Arguably that's a bit heavy-handed, and maybe there is a simpler approach, but I don't know it.
I'm still in a learning phase for Cljs and Om. I'm looking into writing comopnent test. Some components have cljs-http calls to an API I created. When testing, I do not want those API calls to actually send the request, so I'm looking into mocking the request and returning a fixture. Here's an example component I have:
(defn async-component [data owner]
(reify
IWillMount
(will-mount [_]
(let [resp (go ((<! (async-call "/") :body))]
(om/update! data [:objects] resp)))
IRender
(render [_]
[:ul
(om/build-all item-component data)])))
(defn async-call [path]
(http/get path {:keywordize-keys true}))
Please don't mind if the code is actually syntactically correct, I'm just showing the gist of it.
What I now want to do is test this async-component and the API call to see if it will render the fixture that I mock the request with. How is this done? I know cljs.test has the async block to test async code with, but all example show it testing actual code blocks that only have a go in it, not in a larger context.
Here is a way you might use mocking to test your component:
(deftest test-async-component
(cljs.test/async done
(with-redefs
[async-call (fn [path]
(let [mock-ch (async/chan 1)
fixture-data {:body {:fixture-with path :and "foobar"}})]
(async/put! mock-ch fixture-data)
mock-ch)]
; At this point we successfully mocked out our data source (the API call)
; the only task that remains is to render our Om component into DOM and inspect it.
; As this task requires utility fns I will reuse the ones in this blog post:
; http://lab.brightnorth.co.uk/2015/01/27/unit-and-browser-testing-om-clojurescript-applications/
(let [c (new-container!)
initial-data {:objects [{:initial-object 42}]}]
; This will mount and render your component into the DOM residing in c.
(om/root async-component initial-data {:target c})
(testing "fixture data gets put into the DOM"
(is (= "foobar" (text (sel1 c :ul)))))
; You can add more tests in this manner, then finally call 'done'.
(done)))))
The steps taken in the above code in English:
Write async-call's mock fn that returns a channel (the same interface as the original one) prefilled with fixture data.
Mock out the original fn (you need to refer it or fully qualify the ns).
Create a new virtual DOM for unit testing purposes.
Specify the mock data that doesn't come from the API, if any.
Render your component into DOM (this will call async-call when om/will-mount runs, taking the fixture-data off the chan).
Observe DOM contents.
I'm working on a Liberator project in Clojure. I've defined a series of routes which return JSON data computed by logic in some other namespace. I would like to be able to change the namespace that implements the logic programmatically so I can do something like this:
JAVA_OPTS='-DgameLogicNamespace=foo.logic.mock' lein ring server-headless 8080
I am currently doing it like this:
(ns foo.routes
(:require [compojure.core :refer :all]
[liberator.core :as lib :refer [defresource request-method-in]]
[liberator.representation :refer [ring-response]]))
(require
(vec
(cons (symbol (System/getProperty "gameLogicNamespace" "foo.logic.real"))
'[:as logic])))
This works, but feels a bit clunky. Is there an idiomatic way to accomplish what I want?
One of my main motivations is actually for unit testing routes with mock data, so if there's a nice solution for providing the mock logic only in tests (and not as a JVM system property), suggestions are welcome.
One of my main motivations is actually for unit testing routes with mock data, so if there's a nice solution for providing the mock logic only in tests (and not as a JVM system property), suggestions are welcome.
If you haven't already, take a look at ring-mock for some nice utilities to generate mock requests to test your Ring handlers.
If you're interested in providing mock versions of functions that provide the implementation of your application logic during unit tests, consider using with-redefs; it's pretty much custom-made for this purpose.
(ns my-app.handlers-test
(:require [clojure.test]
[my-app.handlers :as h]
[my-app.logic :as l]
[ring.mock.request :as r]))
(deftest test-simple-handler
(with-redefs [l/my-complicated-logic #(update-in % [:a] inc)]
(is (= {:a 2}
(h/my-handler (r/request :post "/foo" {:a 1}))))))
I'm learning about Hystrix and Clojure and don't understand how to (properly) set a timeout on a Hystrix command in Clojure.
I searched StackOverflow and the web more generally. I looked at Hystrix's Clojure wrapper source code (https://github.com/Netflix/Hystrix/blob/master/hystrix-contrib/hystrix-clj/src/main/clojure/com/netflix/hystrix/core.clj). There is a init-fn function parameter that looked promising, but the comments seem to suggest that this won't be a sustainable solution. But would this be a simple start?
I have a ridiculously simple Hystrix command running in Clojure and would appreciate help in extending this to set, say, a 200ms timeout:
(ns hystrix-timeout.core
(:require [clojure.string :as str])
(:require [com.netflix.hystrix.core :as hystrix])
(:gen-class))
(defn my-primary [a]
(if (= a true) (throw (Exception. "Primary failed")) (str "primary: " a)))
(defn my-fallback [a]
(str "fallback: " a))
(hystrix/defcommand my-command
{:hystrix/fallback-fn my-fallback}
[a]
(my-primary a))
(defn -main
"Executes a simple Hystrix command. Will use a timeout when I know how to do this in Clojure."
[& args]
(println (my-command false))
(println (my-command true))
(System/exit 0) ; Exit explicitly as Hystrix threads are still running.
)
I've put my lein project up at https://github.com/oliverbaier/learning/tree/master/hystrix-timeout in case this makes answering easier.
Thanks a lot,
Oliver
The simplest route is to just use System properties. You can set either a global default:
(System/setProperty "hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds" "200")
or a command specific value:
(System/setProperty "hystrix.command.my-command.execution.isolation.thread.timeoutInMilliseconds" "200")
You can perform this in your -main method. If you are running a real web-app sans -main, you can add a ring init in project.clj :ring {:handler your-handler :init your-config-method} your-config-method will be invoked on startup.
The following program, when run from an überjar, exits at the end only when using the in-memory Datomic database; when connecting to the Datomic server, it hangs indefinitely rather than exiting the JVM:
(ns myns.example
(:use [datomic.api :only [db q] :as d])
(:gen-class))
;; WORKS: (def uri "datomic:mem://testdb")
(def uri "datomic:free://localhost:4334/testdb2")
(defn -main []
(println 1)
(when (d/create-database uri)
(d/connect uri))
(shutdown-agents)
(println 2))
Run as:
lein uberjar && java -cp target/myns-0.1.0-SNAPSHOT-standalone.jar myns.example
Outputs:
1
2
and hangs. It only hangs if the DB doesn't exist when the program starts.
Anyone know why, or how to fix? This is with both datomic-free-0.8.4020.26 and datomic-free-0.8.3941.
UPDATE -- the above program does actually terminate, but it takes a very long time (> 1 minute). I'd like to know why.
shutdown-agents takes up to one minute to complete (assuming no agents are running an action).
This is due to the way java.util.concurrent cached thread pools work.
Use datomic.api/shutdown
shutdown
function
Usage: (shutdown shutdown-clojure)
Shut down all peer
resources. This method should be called as part of clean shutdown of
a JVM process. Will release all Connections, and, if shutdown-clojure
is true, will release Clojure resources. Programs written in Clojure
can set shutdown-clojure to false if they manage Clojure resources
(e.g. agents) outside of Datomic; programs written in other JVM
languages should typically set shutdown-clojure to true.
Added in Datomic Clojure version 0.8.3861
(ns myns.example
(:require [datomic.api :as d])
(:gen-class))
(def uri "datomic:free://localhost:4334/testdb2")
(defn -main []
(d/create-database uri)
(let [conn (d/connect uri)]
(try
;; do something
(finally (d/shutdown true)))