Testing Luminous rendering in clojure with core.test - clojure

This code is from the default luminus template:
(deftype RenderableTemplate [template params]
Renderable
(render
[this request]
(content-type
(->>
(assoc
params
(keyword (s/replace template #".html" "-selected"))
"active"
:servlet-context
(:context request)
:user-id
(session/get :user-id)
:user
(session/get :user))
(parser/render-file (str template-path template))
response)
"text/html; charset=utf-8")))
(defn render [template & [params]]
(RenderableTemplate. template params))
And I need to test this function using clojure.test:
(defn home-page [& [user]]
(layout/render
"home.html"
{:user user}))
How will I test the above function with the value associated to the key :user?

I suggest you to read some documentation and make a reasonable effort before launching so general questions. This could be an start http://blog.jayfields.com/2010/08/clojuretest-introduction.html
After you feel a bit comfortable with clojure test you may like to move to https://github.com/marick/Midje/wiki/A-tutorial-introduction-for-Clojure.test-users
Enjoy :)

First set your routes and handlers like this:
(defn home-page [& [user]]
(layout/render
"home.html"
{:user user}))
; setting routes
(defroutes main-routes
(GET "/user/:user" [user] (home-page user)))
Then do the unit testing:
Basic unit test
(deftest home-page-test
; Check if response code is 200
(is (= 200 (:status (main-routes {:request-method :get :uri "/user/Michael"})))))
or you can also use Midje
Using Midje
(:use midje.sweet
clojure.test)
(fact "Homepage Test"
(:status (main-routes {:request-method :get :uri "/user/Michael")) => 200)

I faced the same problem.
Calling (home-page) directly will return a RenderableTemplate type which isn't useful for testing.
In your test require:
(:require [capacityplanning.layout :as layout]
[selmer.parser :as parser])
Add this function:
(def template-path "templates/")
(defn mockRender [template params]
(parser/render-file (str template-path template) params))
And in your test you can bind:
(with-redefs [layout/render mockRender]
(home-page user))
Now when home-page is called it will return the html as a string. This should be more useful to write unit tests against.

Related

with-redefs not replacing valid Compojure route function call in clojure.test

I want to test my Compojure endpoints and see that the required method is called. However, with-redefs-fn is not replacing the method.
Test method-
(ns project-name.handler-test
(:require [clojure.test :refer :all]
[project-name.handler :as handler :refer [retrieve-records test-handler]]
[ring.mock.request :as mock]))
(deftest fn-handler-routes
(with-redefs-fn {#'handler/retrieve-records (fn [arg] :test)}
(let [response {:status 200 :body :test}]
(let [mock-handler (test-handler)]
(is (= response (mock-handler (mock/request :get "/records/name"))))))))
I am guessing routes are static, and thus the method fires and returns actual data instead of calling the redefined function. test-handler is a failing attempt to try to circumvent -- it fires okay, but does not use with-redefs and returns repo data.
Pertinent Source Code-
(defn retrieve-records [arg] (repo/get-records arg))
(defroutes routes
(GET "/" [] (resource-response "index.html" {:root "public"}))
(context "/records" []
(GET "/name" [] (retrieve-records :lastname)))
(resources "/"))
(defn test-handler []
(-> #'routes wrap-reload))
Related Issues
I suspect a similar underlying issue as this SO question about Midje but I am using clojure.test and not midje.
This question is different from a very similar SO question in that the Compojure route is legitimate and I want to return something in the :body, not just test the status (already tried that answer).
This question is different from an integration test question in that it is a handler unit test, though probably the same underlying issue.
Any help would be greatly appreciated.

Dynamic handler update in Clojure Ring/Compojure REPL

I've created a new Compojure Leiningen project using lein new compojure test. Web server is run by lein repl and then
user=> (use 'ring.adapter.jetty)
user=> (run-jetty test.handler/app {:port 3000})
Routes and app handler specification is trivial:
(defroutes app-routes
(GET "/*.do" [] "Dynamic page")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Now, after changing anything in app-routes definition (e.g. changing "Dynamic page" text to anything else, or modifying URI matching string), i do not get the updated text/routes in the browser. But, when changing app-routes definition slightly to
(defn dynfn [] "Dynamic page fn")
(defroutes app-routes
(GET "/*.do" [] (dynfn))
(route/not-found "Not Found"))
i do get dynamic updates when changing the return value of dynfn. Also, following the advice from this article and modifying the app definition to
(def app
(wrap-defaults #'app-routes site-defaults))
(note the #' that transparently creates a var for app-routes) also helps!
Why is that so? Is there any other way one could get a truly dynamic behaviour in defroutes?
Thanks!
#'app-routes is a reader macro that expands to (var app-routes). When a var is used as if it were a function, it is dereferenced anew on each invocation, and then the value returned by that deref is called.
If you were to supply app-routes as the argument, the compiler would give the dereferenced value to wrap-defaults, and when the var is updated, the previous value is not changed, so changing the var does not change the behavior of app.
The following repl transcript might be instructive:
user=> (defn foo [] "original")
#'user/foo
user=> (defn caller [f] #(f))
#'user/caller
user=> (def call-foo-value (caller foo))
#'user/call-foo-value
user=> (call-foo-value)
"original"
user=> (def call-foo-var (caller #'foo))
#'user/call-foo-var
user=> (call-foo-var)
"original"
user=> (defn foo [] "changed")
#'user/foo
user=> (call-foo-value)
"original"
user=> (call-foo-var)
"changed"

ClojureScript circular dependency

I'm struggling with a circular dependency in ClojureScript. I'm trying out this language for a month, haven't ever worked with the real thing (Clojure).
I have a client side app that uses secretary as a router. When I'm defining my routes, they're handler functions, push values to the history-channel, which is then consumed by the main app component that displays particular views. Thus, the values I push from my routes, contain a reference to the view function. This view function are om components that render the given location. In these view functions, I often need to generate links, URLs to other locations in the app. These URLs are generated from the same handler functions that references them. That's how my circular dependency is born. What is an elegant way to resolve it?
router -> views -> router
-- route.cljs
(ns myapp.route
(:require [secretary.core :as secretary :include-macros true :refer [defroute]]
[myapp.views.welcome :as welcome]
[myapp.views.some :as some]))
(defroute home "/" {}
(put! history-chan {:token "/"
:view welcome/view}))
(defroute some "/some" {}
(put! history-chan {:token "/some"
:view some/view}))
-- welcome.cljs
(ns myapp.views.welcome
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]
[myapp.route :as route]))
(defn view [state owner]
(reify
om/IRender
(render [_]
(html [:div [:a {:href (route/some)}]]))))
Circular dependencies in Clojure have no easy or elegant solutions. Most likely you have to restructure your code. You'll have to mess around with it to find something you like. Just off the top of my head I might do something like this:
-- route.cljs
(ns myapp.route
(:require [secretary.core :as secretary :include-macros true :refer [defroute]]
[myapp.views.welcome :as welcome]
[myapp.views.some :as some]))
(defroute home "/" {}
(welcome/route))
(defroute some "/some" {}
(put! history-chan {:token "/some"
:view some/view}))
-- welcome.cljs
(ns myapp.views.welcome
(:require [om.core :as om :include-macros true]
[sablono.core :as html :refer-macros [html]]))
(declare view)
(defn route []
(put! history-chan {:token "/"
:view view}))
(defn view [state owner]
(reify
om/IRender
(render [_]
(html [:div [:a {:href (route)}]]))))
That's just one possible solution.
I had such problem, I don't use route/some to generate urls.
Router (history.cljs) include all views:
[react.nav :as nav]
[react.loading :as loading]
[react.alerts :as alerts]
........
(def history (History.))
(events/listen
history (.-NAVIGATE goog.history.EventType)
(fn [e]
(secretary/dispatch! (.-token e))))
(defroute "alerts" {:as params}
(nav/switch-to "Alerts")
(alerts/display))
Views doesn't require router.
Links works like this:
(def history (History.))
.....
:on-click (fn [e]
(.preventDefault e)
(.setToken history link))}
You can call setToken with any link you need: (str "alerts/" sort-key "/" filter-str)
HTH

Strange boolean behavior in Clojure with checkbox in form-data

I'm using an AngularJS resource to basically $.ajax() some form data.
Post.put({user:$scope.getLoggedInUser(), action:"new"},{body:$scope.postBody, "public":$scope.postPublic}, function(post) {
On the form is a checkbox named "public."
I am using this function to merge the form data with the params from the URL:
(defn get-params [request]
(merge (:params request) (parse-string (slurp (request :body)) true)))
when I println from my route's handler like so I get a nice true when the checkbox is checked:
(println (:public (get-params request)))
However, when I pass the parameter (i.e. not the println) to another function in my controller that talks to the database, I do another println at the beginning of that function and get nil instead.
I've tried passing it as
(read-string x)
(boolean (Boolean/valueOf x))
(Boolean/valueOf x)
to no avail.
One thing that might be causing it (but I don't know why) is that I'm wrapping the request through authentication like this:
(auth? request #(create x y z))
where the (create) function creates a record in the database.
I can't get it to be true no matter what I've tried.
EDIT: more complete code
CONTROLLER
(defn auth? [request callback-true & [callback-false]]
(println (callback-true))
(let [login-response (auth/login request)]
(if (and (not (string? login-response))
login-response)
(callback-true)
(if callback-false
(callback-false)
(json-response (str "{\"auth\":" login-response "}"), 401)))))
(defn create [user logged-in-user body public?]
(if (= logged-in-user user)
(json-response (post-view/single-post (post/create user body public?)))
(json-response "{\"auth\":\"can't create post under different logged in user\"}" 401)))
(defroutes routes
....
(PUT "/api/:user/new" request
(println request)
(auth? request
#(create (:user (request :params))
(:user (request :session))
(:body (get-params request))
(:public (get-params request)))))
....
)
MODEL
(defn create [username body public?]
(println public?)
(when-not (and
(str/blank? body)
(str/blank? username))
(let [user-id (:id (get-id-from-username username))
new-post-id
(:id
(sql/with-connection db
(sql/insert-values :post
[:usr_id :body :public] [user-id body (Boolean/valueOf public?)])))]
(by-id new-post-id))))
Don't call get-params twice. (request :body) returns a stream, which can't be read twice.

ring-json's wrap-json-response middleware and compojure returns text/plain?

I'm trying to use ring-json's wrap-json-response middleware within my compojure app. I have a simple GET handler that returns a map, like {:foo 1}, and when I hit the URL, ring responds with text/plain and an empty response body. I can't seem to get it to respond with the JSON version of the map.
Here's my handler code:
(ns localshop.handler
(:use compojure.core)
(:require [localshop.routes.api.items :as routes-api-items]
[ring.middleware.json :as middleware]
[compojure.handler :as handler]
[compojure.route :as route]))
;; map the route handlers
(defroutes app-routes
(context "/api/item" [] routes-api-items/routes))
;; define the ring application
(def app
(-> (handler/api app-routes)
(middleware/wrap-json-body)
(middleware/wrap-json-params)
(middleware/wrap-json-response)))
The route handler function literally just returns a map, so the code for that is simple enough that I think I could leave out. If returning a map from a compojure route handler is the problem, then perhaps that's it?
Check out this. Basically if you return {:body {:my-map "hello"}} then it will work fine.
Stumbe similar issue, when writing REST API.
When handler return vector, i get exception that no implementation of method render for PersistentVector in Renderable protocol in compojure.
When return map, headers is empty.
When return sequence, i get 'text/html'.
So, i think it's good to be extend Renderable in our code: really nice gift from clojure.
But, as hack, for fast solution, i use next middleware:
(defn wrap-content-json [h]
(fn [req] (assoc-in (h req) [:headers "Content-Type"] "application/json")))