项目作者: lambdahands

项目描述 :
A Clojure(Script) expectation/snapshot testing library, inspired by cram, ppx_expect, and jest
高级语言: Clojure
项目地址: git://github.com/lambdahands/opticlj.git
创建时间: 2017-09-22T05:54:20Z
项目社区:https://github.com/lambdahands/opticlj

开源协议:MIT License

下载


opticlj

opticlj is a Clojure(Script) expectation testing (also known as snapshot testing)
library.

Rationale

Expectations, or snapshots, is an automated testing strategy that captures the
output of a program as a reference to its correctness. In contrast to unit
testing, snapshots don’t require the programmer to specify the correct output
of their program but instead to verify the output.

opticlj let’s you define these snapshots and automatically generate the
outputs into files. If you change the implementation of your program, the output
files may be checked against the new behavior for differences.

I was inspired by this testing strategy because it navigates elegantly between
REPL driven development and testing. Unit testing is often cumbersome, but I’ve
found it to be even more so while writing Clojure code: I often verify the
correctness of my functions by simply evaluating them, but that output doesn’t
persist outside of my own machine.

Snapshot testing may be a way for Clojure developers to cast a wide net over
the correctness of their programs while staying close to the REPL.

Use Cases

Snapshot testing is often a great substitute to unit testing, but it in no way
has the power to verify programs as thoroughly as property-based testing.
Snapshot tests are best used for pure functions, and aren’t recommended in
cases where correctness must be “proven” (big air quotes).

Inspirations

Installation

  1. [opticlj "1.0.0-alpha10"]

See on Clojars

Disclaimer

opticlj is alpha software, and its API is likely subject to change.

Usage

The below example is a way to get started with opticlj in Clojure.

Require the opticlj.core namespace to get started:

  1. (ns my-project.core-test
  2. (:require [opticlj.core :as optic :refer [defoptic]]))

Let’s define a function to test:

  1. (defn add [x y]
  2. (+ x y))

Define an optic like so:

  1. (defoptic ::one-plus-one (add 1 1))

This does two things:

  • Defines “runner” function that can be accessed with opticlj.core/run
  • Writes an output file in test/__optic__/my_project/core_test/one_plus_one.clj

Here’s what one_plus_one.clj looks like:

  1. (in-ns 'my-project.core-test)
  2. (add 1 1)
  3. 2

The in-ns expression allows us to evaluate this file, which is especially
useful if your editor integrates with the REPL.

Next, if we change the implementation of add and re-run the optic, we get
output confirming the snapshot was checked:

  1. (defn add [x y]
  2. (+ x y 2))
  3. (run ::one-plus-one)
  4. ; outputs
  5. {:file "test/__optic__/my_project/core_test/one_plus_one.clj"
  6. :err-file "test/__optic__/my_project/core_test/one_plus_one.err.clj"
  7. :passing? false
  8. :diff {:string "<truncated>"}
  9. :form (add 1 1)
  10. :result 4
  11. :kw :my-project.core-test/one-plus-one}

A new file was created: test/__optic__/my_project/core_test/one_plus_one.err.clj

  1. (in-ns 'my-project.core-test)
  2. (add 1 1)
  3. 4

Also, note how the :passing? key is false. We can view our error diff by
calling optic/errors:

  1. (optic/errors)
  2. ; prints
  3. --- test/__optic__/my_project/core_test/one_plus_one.clj 2017-09-22 16:03:38.000000000 -0500
  4. +++ - 2017-09-22 16:04:38.000000000 -0500
  5. @@ -2,4 +2,4 @@
  6. (add 1 1)
  7. -2
  8. +4

What we get back is essentially the output of running:

  1. echo "...my new output..." | diff -u <output-file> -

Let’s say we wanted to change the rules of our universe and make the addition
of one and one equal to four. We can adjust! our optic to accept these new rules:

  1. (optic/adjust! ::one-plus-one)
  2. ; outputs
  3. {:adjusted {:file "test/__optic__/my_project/core_test/one_plus_one.clj"
  4. :passing? true
  5. :diff nil
  6. :err-file nil
  7. :form (add 1 1)
  8. :result 4
  9. :kw :my-project.core-test/one-plus-one}}

Now when we check for errors, we see we have resolved our new form of arithmetic:

  1. (optic/errors)
  2. ; outputs
  3. nil

ClojureScript

opticlj supports ClojureScript with a few caveats, namely that in order to run
ClojureScript tests, you must output your test code using :target :nodejs in
your compiler options. See the test/opticlj/cljs directory
for an example of using opticlj with the doo
test runner.

A convenience function, opticlj.core/ok?, exists to wrap opticlj’s tests
in a cljs.test/deftest expression. For example:

  1. (ns my-project.cljs.core-test
  2. (:require [cljs.test :as test :refer-macros [deftest]]
  3. [opticlj.core :as optic :refer-macros [defoptic]]))
  4. (defoptic ::two-plus-two (+ 2 2))
  5. (deftest optics
  6. (test/is (optic/passing? (optic/review!))))

Todo

  • Warn if optics is undefined in the program yet exists in a file
  • Add a clean! method to remove unused optics
  • Use defoptic on defoptic (Inception noise)
  • Complete API documentation
  • Reimplement core API with stateless methods