In this article, we will see how to rapidly startup a clojure project using leiningen.
Pre-requisites
GNU/Emacs
See this previous post for installing it
clojure environment
See this previous post
a network to fetch dependencies
Create a new clojure project
lein new hello
This will create an arborescence like this:
/home/tony/repositories/test/hello/ ├── .gitignore ├── project.clj ├── README ├── src │ └── hello │ └── core.clj └── test └── hello └── test └── core.clj 5 directories, 4 files
I usually initialize a git project to hold my modifications.
git init && git add . && git commit -m "Bootstrap clojure project"
Launch a repl
clojure-jack-in
As we already installed emacs, we can launch the repl by doing simply:
M-x cd
to change the current folder emacs runs into thehello folder
(the one containing theproject.clj
file)M-x clojure-jack-in
to launch the repl.
A new buffer must have launched itself (may take some time the first time).
; SLIME 20100404 user>
Basic check
Launch some basic operations and hit enter to check that the repl reacts:
; SLIME 20100404 user> (+ 1 1) 2 user> (reduce + [ 1 2 3 4]) 10 user>
Setup dependencies
project.clj
The project.clj
file is the leiningen file holding the metadata of your project.
This is this file that you want to edit to add dependencies or dev-dependencies.
By default, here is the content:
(defproject hello "1.0.0-SNAPSHOT" :description "FIXME: write description" :dependencies [[org.clojure/clojure "1.3.0"]])
This describes that the project :
- if packaged, the version will be 1.0.0-SNAPSHOT
- a simple description to update
- holds only one runtime dependencies, clojure in its version 1.3.0 (currently, the 1.4.0
has been released).
Note For those from the java world, you can see it as the equivalent of the maven pom.xml with less verbosity!
Add dependencies
dev-dependencies
To add some dev-dependencies, add the :dev-dependencies in this file, like this:
(defproject hello "1.0.0-SNAPSHOT" :description "A simple hello world from clojure" :dependencies [[org.clojure/clojure "1.3.0"]] :dev-dependencies [[midje "1.3.1"]])
or dependencies
As midje is a unit test framework, this must be a dev-dependencies (to avoid having it in the jar at runtime).
But, considering that clojure is extremely expressive, i'd rather make midje a runtime dependencies so that my facts (unit test in midje) stays with the code they tests.
Thus, my facts are another level of documentation (it's quite clear when using marginalia to generate documentations from the code).
If you're like me, then you can change the previous block into this
(defproject hello "1.0.0-SNAPSHOT" :description "A simple hello world from clojure" :dependencies [[org.clojure/clojure "1.3.0"] [midje "1.3.1"]])
Note Another plus side, we do not have any problems concerning the visibility of the functions. A function private is still testable (you do have trouble otherwise).
Setup environment
Rapid check
Launch the unit tests, lein midje
tony@dagobah(0.08,) 09:31:10 ~/repositories/test/hello $ lein midje
>>> Output from clojure.test tests:
FAIL in (replace-me) (core.clj:6)
No tests have been written.
expected: false
actual: false
>>> clojure.test summary:
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
>>> Midje summary:
All claimed facts (0) have been confirmed.
This is a success.
It's normal that the test fail as there is a default false claim in the file test/hello/test/core.clj
(ns hello.test.core (:use [hello.core]) (:use [clojure.test])) (deftest replace-me ;; FIXME: write (is false "No tests have been written."))
You can remove this file as we will write:
- our tests in the source file directly.
- using midje (and not clojure.test, default one)
Add a midje fact
Open the file src/hello/core.clj
.
This contains only the ns
declaration without any dependencies yet.
(ns hello.core)
First of all, we want to solve this problem in tdd. So a first step, is to add a framework that permits that, i chose to use midje.
For this, we add this namespace like this
(ns hello.core (:use [midje.sweet]))
To check that all is ok, we can add a false fact the midje way.
(fact (+ 1 1) => 3)
Note: We can read this fact like this: "This is a fact that (+ 1 1) gives 3" We all know it's false but bare with me.
In the terminal, launch lein midje
again
tony@dagobah(0.14,) 10:21:37 ~/repositories/test/hello (master) $ lein midje FAIL at (core.clj:4) Expected: 3 Actual: 2 FAILURE: 1 fact was not confirmed.
Ok, the test is failing but it's ok, that's what we want.
Now, the ultimate test, check the output when the test is ok.
For this, change the fact with the right result.
(fact (+ 1 1) => 2)
Note This is a fact that (+ 1 1) gives 2.
Now launch lein midje
tony@dagobah(0.53,) 10:21:58 (1) ~/repositories/test/hello (master) $ lein midje All claimed facts (1) have been confirmed.
Another way of checking midje facts
Compilation
When in the core.clj
buffer, C-c C-k
launches the compilation of all the clj file.
The output of this compilation appears in the repl.
When in error:
; SLIME 20100404 [31mFAIL[0m at (core.clj:4) Expected: 3 Actual: 2 user>
When there is no error, nothing appears (except if there are prints in your code).
Check one fact
When in the core.clj
buffer, =C-c ,= launch the evaluation of the fact.
If the fact is true, there will be a quotation just before the fact.
;.;. Any intelligent fool can make things bigger, more complex, and more violent. It takes a touch of genius -- and a lot of ;.;. courage -- to move in the opposite direction. -- Schumacher (fact (+ 1 1) => 2)
Else, there will be a summary of the error
;.;. FAIL at (NO_SOURCE_FILE:1) ;.;. Expected: 3 ;.;. Actual: 2 (fact (+ 1 1) => 3)
Ultimate way
There is an ultimate way for testing with midje that i prefer above all lein midje --lazytest
At the moment, it seems there is a problem with my platform around the leiningen 1.7.1 version (by downgrading it to leiningen 1.6.2, this works) that i was not yet able to solve.
This mode permits to relaunch the facts after a modification on the file system has been done. This is quite handy to avoid the manual compilation. The output is the same as previously described.
When this work on my machine, I use it by opening a terminal buffer (M-x multi-term
) in emacs in which i launch the
lein midje --lazytest
command.
Try lazytest
For those with leiningen 1.6.2 who wants to try it.
- Change the project.clj file to this
(defproject hello "1.0.0-SNAPSHOT" :description "A simple project" :dependencies [[org.clojure/clojure "1.3.0"] [midje "1.3.1"]] :dev-dependencies [[com.intelie/lazytest "1.0.0-SNAPSHOT" :exclusions [swank-clojure]]])
- Relaunch
lein deps
. - Restart
emacs
andclojure-jack-in
- Open a new buffer with
multi-term
orshell
oreshell
or whatever (M-x the-mode-you-chose
). - Then launch the command in the terminal as said previously
Conclusion
You're now able to setup a project in clojure and knows a little about midje.
In a near future, i intend to make some other blog posts to focus on:
- a simple problem resolution using top down tdd in clojure with midje
- continued integration with travis-ci
- heroku for the deploying part
- marginalia for the documentation generation and the github integration.