It's a TDD/BDD framework for Clojure and Clojurescript, based on RSpec.
Installation | Clojure | ClojureScript
NOTE: Speclj 3.3+ requires Clojure 1.7+.
lein new speclj YOUR_PROJECT_NAME
@trptcolin's speclj template will generate all the files you need.
Or, if you're using ClojureScript:
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.11.3"]]
:profiles {:dev {:dependencies [[speclj "3.4.8"]]}}
:plugins [[speclj "3.4.8"]]
:test-paths ["spec"]
$ 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
).
deps.edn
Add a spec
alias to your deps.edn
.
{
:aliases {:spec {:main-opts ["-m" "speclj.main" "-c"]
:extra-deps {speclj/speclj {:mvn/version "3.4.8"}}
:extra-paths ["spec"]}}
}
Run specs.
clj -M:spec # printing dots
clj -M:spec -a # auto running with doc output
clj -M:spec <OPTIONS>
Speclj includes a Leiningen task.
$ lein spec <OPTIONS>
lein run
Speclj also includes a Clojure main namespace:
$ lein run -m speclj.main <OPTIONS>
And sometimes it's just easier to run a Java command, like from an IDE.
$ 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.
$ lein spec -a
There are several options for the runners. Use the --help
options to see them all.
$ lein spec --help
:eval-in
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]))
$ lein cljs
The command below will start a process that will watch the source files and run specs for any updated files.
$ bin/speclj path/to/compiled.js
Speclj integrated with Cloverage for all your code coverage needs. Make sure
speclj 3.4.6 or above is included in the classpath and use Cloverage's --runner :speclj
command line option.
Here's an example alias for your deps.edn
.
{:aliases {:cov {:main-opts ["-m" "cloverage.coverage" "--runner" ":speclj" "-p" "src" "-s" "spec" ]
:extra-deps {cloverage/cloverage {:mvn/version "1.2.4"}
speclj/speclj {:mvn/version "3.4.8"}}}}}
Sadly, Cloverage doesn't offer a way to pass arguments to the runner (Speclj in this case). Speclj will use the
standard runner and progress reporter by default. If you'd like different options, you can use the speclj.cloverage
namespace as a model to create your own cloverage/speclj runner in your project.
Clone the master branch, build, and run all the tests:
$ 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:
npm install
clj -T:build clean
clj -M:test:cljs
To include in a local project
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:
Copyright (C) 2010-2023 Micah Martin All Rights Reserved.
Distributed under the The MIT License.