Skip to content
Oeiuwq Faith Blog OpenSource Porfolio

slagyr/speclj

pronounced "speckle": a TDD/BDD framework for Clojure.

slagyr/speclj.json
{
"createdAt": "2010-11-09T01:45:42Z",
"defaultBranch": "master",
"description": "pronounced \"speckle\": a TDD/BDD framework for Clojure.",
"fullName": "slagyr/speclj",
"homepage": "",
"language": "Clojure",
"name": "speclj",
"pushedAt": "2025-10-10T15:42:24Z",
"stargazersCount": 475,
"topics": [],
"updatedAt": "2025-11-11T07:33:36Z",
"url": "https://github.com/slagyr/speclj"
}

It’s a TDD/BDD framework for Clojure and Clojurescript, based on RSpec.

Speclj Build

[Installation]!(#installation) | [Clojure]!(#clojure) | [ClojureScript]!(#clojurescript) | [Clojure CLR]!(#clojure-clr) | [Babashka]!(#babashka)

Clojars Project

NOTE: Speclj 3.3+ requires Clojure 1.7+.

Terminal window
lein new speclj YOUR_PROJECT_NAME

@trptcolin’s speclj template will generate all the files you need.

Or, if you’re using ClojureScript:

Terminal window
lein new specljs YOUR_PROJECT_NAME

@ecmendenhall’s specljs template will save you lots of time by getting you started with a running Clojure & ClojureScript setup.

Include speclj in your :dev profile :dependencies and:plugins. Then change the :test-paths to "spec"

; - snip
:dependencies [[org.clojure/clojure "1.12.0"]]
:profiles {:dev {:dependencies [[speclj "3.12.0"]]}}
:plugins [[speclj "3.12.0"]]
:test-paths ["spec"]
  1. Check out the source code: https://github.com/slagyr/speclj
  2. Install it:
Terminal window
$ clj -T:build install

Start with the speclj.core namespace. That is Speclj’s API and it’s very unlikely you’ll need anything else.

All your speclj code should go into a directory named spec at the root of your project. Conventionally, the spec directory will mirror the src directory structure except that all the spec files will have the ‘_spec.clj’ postfix.

| sample_project
|-- project.clj
|-- src
|-- sample
|-- core.clj
| (All your other source code)
|-- spec
|-- sample
|-- core_spec.clj
| (All your other test code)

Checkout this example spec file. It would be located at sample_project/spec/sample/core_spec.clj. Below we’ll look at it piece by piece.

(ns sample.core-spec
(:require [speclj.core :refer :all]
[sample.core :refer :all]))
(describe "Truth"
(it "is true"
(should true))
(it "is not false"
(should-not false)))
(run-specs)

Your spec files should :require the speclj.core in it’s entirety. It’s a clean namespace and you’re likely going to use all the definitions within it. Don’t forget to pull in the library that you’re testing as well (sample.core in this case).

(require '[speclj.core :refer :all])
(require '[sample.core :refer :all])

describe is the outermost container for specs. It takes a String name and any number of spec components.

(describe "Truth" ...)

it specifies a characteristic of the subject. This is where assertions go. Be sure to provide good names as the first parameter of it calls.

(it "is true" ...)

Assertions. All assertions begin with should. should and should-not are just two of the many assertions available. They both take expressions that they will check for truthy-ness and falsy-ness respectively.

(should ...)
(should-not ...)

At the very end of the file is an invocation of (run-specs). This will invoke the specs and print a summary. When running a suite of specs, this call is benign.

(run-specs)

There are many ways to make assertions. Check out the API Documentation. Take note of everything that starts with should.

it or characteristics are just one of several spec components allowed in a describe. Others like before, with, around, etc are helpful in keeping your specs clean and dry. The same API Documentation lists the spec component (everything that doesn’t start with should).

Add a spec alias to your deps.edn.

{
:aliases {:spec {:main-opts ["-m" "speclj.main" "-c"]
:extra-deps {speclj/speclj {:mvn/version "3.12.0"}}
:extra-paths ["spec"]}}
}

Run specs.

Terminal window
clj -M:spec # printing dots
clj -M:spec -a # auto running with doc output
clj -M:spec <OPTIONS>

Speclj includes a Leiningen task.

Terminal window
$ lein spec <OPTIONS>

Speclj also includes a Clojure main namespace:

Terminal window
$ lein run -m speclj.main <OPTIONS>

And sometimes it’s just easier to run a Java command, like from an IDE.

Terminal window
$ java -cp <...> speclj.main <OPTIONS>
$ java -cp `lein classpath` speclj.main

The -a options invokes the “vigilant” auto-runner. This command will run all your specs, and then wait. When you save any test(ed) code, it will run all the affected specs, and wait again. It’s HIGHLY recommended.

Terminal window
$ lein spec -a

There are several options for the runners. Use the --help options to see them all.

Terminal window
$ lein spec --help

When using lein spec you can get a little faster startup by adding :speclj-eval-in :leiningen to your project map. It will prevent Leiningen from spinning up another Java process and instead run the specs in Leiningen’s process. Use at your own risk.

All your speclj code should go into a a directory named spec at the root of your project. Conventionally, the spec directory will mirror the src directory structure except that all the spec files will have the ‘_spec.cljs’ postfix.

| sample_project
|-- project.clj
|-- bin
|-- speclj.js
|-- src
|-- cljs
|-- sample
|-- core.cljs
| (All your other source code)
|-- spec
|-- cljs
|-- sample
|-- core_spec.cljs
| (All your other test code)

lein-cljsbuild is a Leiningen plugin that’ll get you up and running with ClojureScript. You’ll need to add a :cljsbuild configuration map to your project.clj.

:plugins [[lein-cljsbuild "1.0.3"]]
:cljsbuild {:builds {:dev {:source-paths ["src/cljs" "spec/cljs"]
:compiler {:output-to "path/to/compiled.js"}
:notify-command ["phantomjs" "bin/speclj" "path/to/compiled.js"]}
:prod {:source-paths ["src/cljs"]
:compiler {:output-to "path/to/prod.js"
:optimizations :simple}}}
:test-commands {"test" ["phantomjs" "bin/speclj" "path/to/compiled.js"]}}

Speclj works by operating on your compiled ClojureScript. The :notify-command will execute the bin/speclj command after your cljs is compiled. The bin/speclj command will use speclj to evaluate your compiled ClojureScript.

Create a file named speclj in your bin directory and copy the code below:

#! /usr/bin/env phantomjs
var fs = require("fs");
var p = require('webpage').create();
var sys = require('system');
p.onConsoleMessage = function (x) {
fs.write("/dev/stdout", x, "w");
};
p.injectJs(phantom.args[0]);
var result = p.evaluate(function () {
speclj.run.standard.arm();
return speclj.run.standard.run_specs("color", true);
});
phantom.exit(result);

Checkout this example spec file. It would be located at sample_project/spec/cljs/sample/core_spec.cljs. Below we’ll look at it piece by piece.

(ns sample.core-spec
(:require-macros [speclj.core :refer [describe it should should-not run-specs]])
(:require [speclj.core]
[sample.core :as my-core]))
(describe "Truth"
(it "is true"
(should true))
(it "is not false"
(should-not false)))
(run-specs)

You’ll need to :require-macros the speclj.core namespace and :refer each speclj test word that you want to use. In the example below, we are using describe, it, should, should-not, and run-spec. Yes, this is unfortunate, but unavoidable. If you wanted to use context you would simply add it to the current :refer collection. For a list of speclj test words go to the API Documentation

Your spec files must :require the speclj.core too, even though we don’t alias it or refer anything. Don’t forget this! It loads all the needed speclj namespaces. Also pull in the library that you’re testing (sample.core in this case).

As a final note, when requiring your tested namespaces (sample.core in this case), you’ll probabaly want to alias it using :as.

(:require-macros [speclj.core :refer [describe it should should-not run-specs])
(:require [speclj.core]
[sample.core :as my-core]))
Terminal window
$ lein cljs

The command below will start a process that will watch the source files and run specs for any updated files.

Terminal window
$ bin/speclj path/to/compiled.js

Install Clojure CLR and Clojure CLR CLI.

Add a spec alias to your deps-clr.edn.

{
:aliases {:spec {:main-opts ["-m" "speclj.main" "-c"]
:extra-deps {io.github.slagyr/speclj {:git/tag "3.12.0" :git/sha "176d026q"}}
:extra-paths ["spec"]}}
}

Run specs.

Terminal window
cljr -M:spec # printing dots
cljr -M:spec -a # auto running with doc output
cljr -M:spec <OPTIONS>

Install Babashka.

Add a spec task to your bb.edn.

{:paths ["src/bb" "spec/bb"]
:tasks {spec {:extra-deps {speclj/speclj {:mvn/version "3.12.0"}}
:requires ([speclj.main :as main])
:task (apply main/-main "-c" "spec/bb" *command-line-args*)}}
}

Run specs.

Terminal window
bb spec # printing dots
bb spec -a # auto running with doc output
bb spec <OPTIONS>

Speclj integrated with Cloverage for all your code coverage needs. Make sure speclj 3.4.6 or above is included in the classpath.

Here’s an example alias for your deps.edn.

{:aliases {:cov {:main-opts ["-m" "speclj.cloverage" "--" "-p" "src" "-s" "spec"]
:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}
speclj/speclj {:mvn/version "3.12.0"}}}}}

To pass arguments to speclj, include them as you would with speclj.main before the double-hyphen "--":

{:aliases {:cov {:main-opts ["-m" "speclj.cloverage" "-c" "-t" "~slow" "--" "-p" "src" "-p" "spec"]}}}

Clone the master branch, build, and run all the tests:

Terminal window
$ git clone https://github.com/slagyr/speclj.git
$ cd speclj
$ clj -T:build javac
$ clj -M:test:spec

To make sure tests pass ClojureScript too, make sure you have npm:

Terminal window
npm install
clj -T:build clean
clj -M:test:cljs

To include in a local project

Terminal window
clj -T:build clean
clj -T:build javac
clj -T:build jar

In deps.edn

{speclj/speclj {:local/root "/path/to/speclj/target/speclj-3.4.6.jar"}}

Make patches and submit them along with an issue (see below).

Post issues on the speclj github project:

  • Speclj 2.* requires Clojure 1.4.0+
  • Clojure 1.3 is not supported by any version of Speclj due to a bug in Clojure 1.3.

Copyright (C) 2010-2023 Micah Martin All Rights Reserved.

Distributed under the The MIT License.