I'm trying to truncate my dataomic database between tests. I see a lot of questions on google about excision and how to delete data, but none about just wiping a database clean.
I'm doing something along the following using core.test:
(with-redefs [db/uri "datomic:free://localhost:4334/test_om_asyn101_next"
db/conn (d/connect db/uri)]
(run-tests 'rtest.core-test))
I've been wiping out the DB by changing the URI and creating anew, but getting tired of doing that! Thanks!
There is a delete-database fn. See day of datomic tutorial for an example about how to create a new in memory db for each test.
Also, yeller has a nice example of how to use datomic's "what if" functionality to do unit testing.
I've been using (delete-database) as dAni suggests, with a clojure.test fixture:
(ns test-util)
(defn datomic-rollback-fixture [test-fn]
;; setup
(run-migrations)
;; run the tests
(test-fn)
;; clean up
(let [datomic-uri (get-in config/config [:datomic :uri])]
(when (string/starts-with? datomic-uri "datomic:mem:")
(d/delete-database datomic-uri))))
The (run-migrations) function loads our schema definitions from an EDN file, which we have defined via conformity. The bit about only destroying a datomic:mem database is because I'm paranoid about deleting databases out of production by accident.
Then in the test functions, we've got:
(use-fixtures :each test-util/datomic-rollback-fixture)
(deftest my-test ...)
Seems to work fine for me so far and is plenty fast enough with the memory database.
Related
When running a query on a postgres database on a clojure REPL, the timestamps fields are presented in UTC and I need them to be in timezone America/Sao_Paulo (UTC-3)
So far I have tried the following on Intellij's REPL:
Set -Duser.timezone=America/Sao_Paulo inside the file idea.vmoptions (intellij's)
Add :jvm-opts ["-Duser.timezone=America/Sao_Paulo"] to project.clj
Add -Duser.timezone=America/Sao_Paulo in Intellij's REPL configuration
export JAVA_OPTS="-Duser.timezone=America/Sao_Paulo:$JAVA_OPTS" inside ~/.zshrc
and the following on Leiningen REPL:
Add :jvm-opts ["-Duser.timezone=America/Sao_Paulo"] to project.clj
export JAVA_OPTS="-Duser.timezone=America/Sao_Paulo:$JAVA_OPTS" inside ~/.zshrc
None worked so far!
Sample code
(ns experiments
(:require [next.jdbc :as jdbc]))
(def db
{:dbtype "postgres"
:dbname "<dbname>"
:host "<host>"
:port 5432
:user "<user>"
:password "<pass>"})
(def ds (jdbc/get-datasource db))
(jdbc/execute! ds ["select current_timestamp"])
You did not mention any Postgres options. Please study this page carefully for info and options.
If the above does not solve your problem, it may be easiest to use java.time to do the conversion. I also have some helper/convenience functions available here. Unit tests show them in action, and the source code provides examples of java.time interop from clojure.
I would avoid the older Joda Time libraries as they are obsolete (replaced by java.time). I think that Java interop is the easiest & most straightforward way to access java.time.
I just finished my first six weeks working with Clojure and so far I'm pretty happy with the language. I'm developing my personal blog with leiningen and PostgreSQL. I already can publish new content, upload files and I have sessions, cookies and roles, anyway I think at this point I have enough code to start to worry about the testing section, but I'm kind of stuck since looks like a lot of things are happening in the clojure's testing and spec side.
So I have this function:
(defn download
"GET /admin/uploads/download/:id"
[params]
(let [id (-> params :id)
upload (model-upload/get-upload id)
filename (:filename upload)
body (clojure.java.io/file (str "public/uploads/" filename))]
{:status 200
:body body
:headers {"Content-Type" "application/pdf"
"Content-Length" (str (.length body))
"Cache-Control" "no-cache"
"Content-Disposition" (str "attachment; filename=" filename)}}))
The function takes a map as argument and delivers a final map to be sent and processed by compojure. I come from a Rails world so the way to test this function in Rails would be to create a FactoryGirl class, create a Rspec model file with the classic:
expect(first_map).to eq(map_returned_by_function)
in it comparing what is expected, and then to run the rspec from the command line to get the green or red line.
Since yesterday I'm trying to replicate that process with Clojure using this doc:
https://www.codesai.com/2018/03/kata-generating-bingo-cards
but I think there is not yet a "standard" way to do a test including the DB (CRUD) part in Clojure. I don't even know where to put the spec files. I see Clojure libraries similar to FactoryGirl but I don't know if I should create my own data structures with spec so I'm not sure where to start, there are clojure.test.check.generators and spec generators but I don't know if they are different or if I should use only spec but not clojure.test.check. Can I run a test from the command line and not inside the REPL?
I mean: is there a document or tutorial about how to test a set of CRUD functions? I think I just need the initial HOWTO and then I could take it from there and I'll write a tutorial to newbies like me.
UPDATED:
It looks like Midje is what I'm looking for:
https://github.com/marick/Midje/wiki/A-tutorial-introduction
It's idiomatic in Clojure to push IO to the edges of your application. Instead of reading from the DB inside your download function, you pass in the data read from the DB into your download function in the param map. Then you write your tests against the pure part.
Your function would end up looking like this:
(defn download-without-db
"GET /admin/uploads/download/:id"
[params]
(let [upload (-> params :upload)
filename (:filename upload)
body (clojure.java.io/file (str "public/uploads/" filename))]
{:status 200
:body body
:headers {"Content-Type" "application/pdf"
"Content-Length" (str (.length body))
"Cache-Control" "no-cache"
"Content-Disposition" (str "attachment; filename=" filename)}}))
(defn get-upload-from-db [params]
(assoc params :upload (-> params :id model-upload/get-upload)))
(defn download [params]
(-> params
get-upload-from-db
download-without-db))
You're just looking for clojure.test. It even mentions relationships with RSpec in its doc.
This is included as part of Clojure itself, no dependencies are needed, and I'd recommend you get familiar with it first before using a non standard test framework like Midje, since it is the defacto test framework for Clojure, and the most popular one.
You would write a test as such:
(deftest download
(testing "With valid input"
(testing "it should return a header map with filename included"
(is (= first_map (unit/download {:id 1}))))))
Now, Clojure is not object oriented, so there are no objects to mock. That said, you often use Java form within Clojure, and Java provides classes and objects. If you want to mock them easily, you can use the Java mocking framework called Mockito.
In your case though, the download function does not use any Java objects. So you don't need too.
Now, if you want this to be an integration test, the test I wrote is good enough for you. If you want this to be a unit test, and I assume (model-upload/get-upload id) does some IO, you'll want to mock the model-upload/get-upload function. You can easily do this using with-redefs-fn:
(deftest download
(testing "With valid input"
(testing "it should return a header map with filename included"
(with-redefs-fn {#'model-upload/get-upload (constantly {:filename "cool.pdf"})}
(is (= first_map (unit/download {:id 1})))))))
Or you can use with-redefs:
(deftest download
(with-redefs [model-upload/get-upload (constantly {:filename "cool.pdf"})]
(testing "With valid input"
(testing "it should return a header map with filename included"
(is (= first_map (unit/download {:id 1})))))))
Here are some online resources for you:
http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-cdocs-summary.html
http://clojuredocs.org/
http://clojure-doc.org/
https://www.braveclojure.com/
https://pragprog.com/book/dswdcloj2/web-development-with-clojure-second-edition
and the following is an example of how I like to structure general tests (not CRUD, though):
https://github.com/cloojure/tupelo/blob/master/test/tst/tupelo/misc.cljc
How do I intitailze my database so my (def db ...) will not attempt to initialize the database when running my unit-tests (since it will not be a database there then).
I mock the the sql and dml in functions (with-redefs) but all examples i have found so far simply define the database directly in the namespace (eg not wrapped in a function).
I strongly recommend you do not put your database in a Var. It's a very tempting, but quite harmful, form of environment coupling.
The recommended way is to pass the database your database as an argument of whatever function needs access to it in its body:
;; BAD
(def db ...)
(defn find-user [user-id]
(run-query find-user-query db user-id))
;; GOOD
(defn find-user [db user-id]
(run-query find-user-query db user-id))
It's not as tedious as you'd think, and the benefits are great (for testing, repl-driven development, etc.).
NOTE:
In the early years of the Clojure community, people have been using dynamic Vars to avoid having to add a parameter all the time.
(def ^:dynamic db nil)
(binding [db ...]
(find-user user-id))
Then we learned to stop doing that :) complecting your logic with its environment is just a bad foundation for your programs.
I am trying to create a web app in clojure.
i have used clojurescript om and react.
there are two files core.cljs and db.clj.
core.cljs contains ui for login page and db.clj contains all database connections.
Now i am trying to call a db.clj method add-user[username password] in
core.cljs.
In db.clj
(defn add-user [username,password]
(sql/with-connection db
(sql/insert-values :users [:username :password]
[username password])))
In core.cljs
(dom/button #js {:ref "submit"
:onClick (fn[e](add-user usname passwrd))}"submit")
But i am not able to call that method in core.cljs.
It shows some error message like
clojure.lang.ExceptionInfo : failed compiling file:src\login_page\core.cljs
clojure.lang.ExceptionInfo : No such namespace: login_page.db, could not locate login_page/db.cljs, login_page/db.cljc, or Closure namespace "login_page.db"
Rename db.clj to either db.cljs or db.cljc. That should get you past the 'No such namespace' error message.
That's the basic gist of it. Of course your dependencies on clj libraries will have to be removed - that might be the reason for the negative comment below. Alter your code so that you use a simple atom as your database. That should get you developing.
And you can wait for a much better answer than this one that will show you how to get the Client and Server communication setup. But it may not come because, as pointed out in the comments, there is already documentation for this, and unfortunately quite a few choices that have to be made. Another unfortunate thing is that the way to do it now may not be the way to do it early next year. Watch the Om-Next space!
I've never had any problems compiling .cljs or .cljc files. You just have to set up your lein project.clj file properly. There will be plenty of examples if you Google around, or you can take a look at the following small Github project: https://github.com/chrismurrph/passing-time - no need to worry about the code, just look at its project.clj file.
I am converting an older web app I made a few months ago from Noir to Compojure and I am using the Lib-Noir add-on. It appears that session/put! is either changed in some way I don't understand or it is bugging out for whatever reason.
Here, I can see that 4Clojure appears to be using it with no problems: See Line 51. I also found this thread that covers the same question but there doesn't appear to be a satisfactory response.
This should work (Noir):
user=> (require '[noir.session :as sesh])
nil
user=> (sesh/put! :user "me")
ClassCastException clojure.lang.Var$Unbound cannot be cast to clojure.lang.Atom
clojure.core/swap! (core.clj:2162)
The above is the same error that I am looking at on the webpage. Basically I'm stuck.
Edit to add
Appears I created a bit of confusion with the command line part: (put!) is not working in the program either. There's not much to write about it, except that it is (shesh/put! :uname user) and it appears that :uname isn't working. I'm confused as to why it would have worked before and not now when I am using the same tools as before. This is a rewrite of a site I build about 6 months ago. I'm just moving it to Compojure from Noir. The lib-noir session is, as far as I know, essentially the same as what was in Noir.
ANOTHER EDIT
I put the code up on github. This isn't the completed project, but hopefully someone can decipher what is going on here: https://github.com/dt1/SoloResume
If you run it from the REPL, there is no browser session registered in Noir. You can simulate this by using binding:
(binding [sesh/*noir-session* (atom {:somekey "somevalue"})]
(sesh/put! :user "borkdude"))
Use this only for testing/simulating to see what goes on in the session map, not in production code.
A fairly old question, but answering here as it was the first Google result when I had the same problem. I was using compojure:1.1.6, ring:1.2.1 and lib-noir:0.7.6
You need to use noir.session/wrap-noir-session when defining your app - e.g:
(def app
(-> (handler/site (routes app-routes ))
session/wrap-noir-session
wrap-base-url))
References:
https://groups.google.com/d/msg/clojure/XXgSGhF912I/luhN9wmMoi8J