I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

Clojure libraries and builds with Leiningen

12.27.2011
| 5598 views |
  • submit to reddit

Leiningen is an automation tool for Clojure projects: while you could in theory continue to use Ant and Maven directly to download JARs dependencies and to run tasks such as the project's test suite, Leiningen allows you to write just Clojure code, and generate on the fly everything else.

In short, Leiningen will use Ant and Maven under the covers, and follow the Maven model of downloading and caching dependencies globally instead of storing them along with your source code. But you won't see under the hood.

Installation

Assuming you're using a Linux system like Ubuntu and the shipped Clojure copy, installing the leiningen package will bring you the lein command.

Alternatively, you can download a single script from the bin/folder, place it on your path and make it executable. On the first execution it will bootstrap itself by downloading the missing dependencies.

There are also installation instruction for Windows boxes, both for the self-installing version and a standalone one.

A walkthrough: creating a new project

Leiningen can create a simple skeleton for our project:

[08:37:53][giorgio@Desmond:~/code]$ lein new clojure-midje-example
Created new project in: /home/giorgio/code/clojure-midje-example
[08:38:01][giorgio@Desmond:~]$ cd code/clojure-midje-example
[08:38:29][giorgio@Desmond:~/code/clojure-midje-example]$ tree
.
├── project.clj
├── README
├── src
│   └── clojure_midje_example
│       └── core.clj
└── test
    └── clojure_midje_example
        └── test
            └── core.clj

5 directories, 4 files

We see a test and src folder, in the best Java/JVM tradition. The source file created as examples follow the folder structure as namespaces:

[09:00:17][giorgio@Desmond:~/code/clojure-midje-example]$ cat test/clojure_midje_example/test/core.clj
(ns clojure-midje-example.test.core
  (:use [clojure-midje-example.core])
  (:use [clojure.test]))

(deftest replace-me ;; FIXME: write
  (is false "No tests have been written."))

You can already try to execute the failing test:

[09:02:52][giorgio@Desmond:~/code/clojure-midje-example]$ lein test
Copying 1 file to /home/giorgio/code/clojure-midje-example/lib
Testing clojure-midje-example.test.core
FAIL in (replace-me) (core.clj:6)
No tests have been written.
expected: false
  actual: false
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.

And of course, write a real test:

[09:05:05][giorgio@Desmond:~/code/clojure-midje-example]$ cat test/clojure_midje_example/test/core.clj
(ns clojure-midje-example.test.core
  (:use [clojure-midje-example.core])
  (:use [clojure.test]))

(deftest the-simplest-test-you-can-write
  (is 3 (+ 1 2)))
[09:05:08][giorgio@Desmond:~/code/clojure-midje-example]$ lein testTesting clojure-midje-example.test.core
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Leiningen also produces a .gitignore file that allows you to ignore compiled classes, any kind of JAR, generated XML and Leiningen's own metadata.

Installing a library: Midje

Midje is a testing framework for Clojure, that I want to install both for being able to try it out later and for demonstrating how Leiningen can pull in a library.

The project.clj file contains all the definitions of dependencies that Leiningen will use to grab JARs. The format of :dependencies is a list of 2-element lists, where each 2-element list contains the name of the library and the required version as a string.

[09:06:11][giorgio@Desmond:~/code/clojure-midje-example]$ cat project.clj
(defproject clojure-midje-example "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]])

By default, Leiningen will grab packages from Clojars, the main community repository for Clojure libraries, and from Maven Central.

Let's fix our project.clj to include midje and midje-lein, two JARs that will make Midje available to the current project. The first is the actual library, while the second provides the lein midje command, a nicer version of lein test.

[09:12:52][giorgio@Desmond:~/code/clojure-midje-example]$ cat project.clj
(defproject clojure-midje-example "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [midje "1.3.0"]]
  :dev-dependencies [[lein-midje "1.0.7"]])

I put midje in :dependencies just to show a different option for libraries, but it should actually be put in :dev-dependencies. The difference is in the latter case it won't be shipped in the JAR package of the project, nor being pulled into projects that depend on the current one.

To validate the availability of midje, let's write the simplest midje test:

[09:17:04][giorgio@Desmond:~/code/clojure-midje-example]$ cat test/clojure_midje_example/test/core.clj
(ns clojure-midje-example.test.core
  (:use [clojure-midje-example.core])
  (:use [clojure.test])
  (:use [midje.sweet]))

(deftest the-simplest-test-you-can-write
  (is 3 (+ 1 2)))
(fact (+ 1 2) => 3)
[09:16:55][giorgio@Desmond:~/code/clojure-midje-example]$ lein midje>>> clojure.test summary:
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
>>> Midje summary:
All claimed facts (1) have been confirmed.

More lein commands

I have posted this example on Github, both for your and mine future reference.

For the ones of you that don't come from a JVM background, don't try to run .clj files in isolation: lein should always be used as a wrapper for him to be able to place the dependencies on the classpath.

Lein provides many other commands for this goal; for example:

  • lein run will call the -main function in the namespace defined with :main in project.clj (it's easy to show than to explain: see the source code.)
  • lein help run will display information about the run command; substitute your favorite command to learn all its options and how it interacts with project.clj.
  • lein jar and lein uberjar will produce a JAR and a standalone JAR respectively for distributing your code.

Conclusions

You don't see it in this output sample, but lein midje also provides colors (a green bar), since it is a custom Leiningen task. The actual usage of Midje is out of the scope of this article, but I hope to have provided a simple way to grab dependencies inside your project.

Lein is easy to use, and provides enough documentation for basic use cases; in integrates testing as a first class task. However error messages are somewhat cryptic, and this is typical of Clojure: make sure to take small steps in order to easily locate any error in the last lines you wrote.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)