• Stars
    star
    109
  • Rank 319,077 (Top 7 %)
  • Language
    Clojure
  • Created over 12 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generic IO playback for clojure

vcr-clj

Circle CI

vcr-clj is a general function recording/playback library for Clojure. It is intended to be used when testing code that does I/O, to achieve several goals:

  • Repeatable tests despite unreliable/unpredictable I/O sources
  • Tests that can be run without requiring the availability of I/O sources
  • Tests that involve realistic data

Any clojure function (er, var) can be recorded and played back.

Requirements

vcr-clj requires Clojure 1.4 or later.

Obtention

[com.gfredericks/vcr-clj "0.4.22"]

Usage

An example with clojure.test:

(ns my.project-test
  (:require [clojure.test :refer :all]
            [vcr-clj.core :refer [with-cassette]]))

(deftest here-is-my-test
  (with-cassette :foo [{:var #'my.io.ns/get-data, ...extra options...}]
    ... do some testy things ...
    ... that call my.io.ns/get-data ...))

There is also currently a separate namespace for recording clj-http requests in particular:

(ns my.project-test
  (:require [clojure.test :refer :all]
            [vcr-clj.clj-http :refer [with-cassette]]))

(deftest here-is-my-webby-test
  (with-cassette :foo
    ... do some testy things ...
    ... that call clj-http functions ...))

(deftest this-test-only-records-some-calls
  (with-cassette {:name :foo-2
                  :recordable? (fn [req] (worth-recording? req))}
    ... do some testy things ...
    ... that call clj-http functions ...))

The first time you run a with-cassette block, a cassette file is created in the /cassettes directory. Each subsequent time, playback is performed using the cassette in the directory. You can delete it to force a re-record.

Namespaced keywords can be used to group cassettes in the filesystem.

Customizing

Each var that is recorded can be customized with options:

  • :arg-transformer: A function with the same argument signature as the recorded function, which returns a vector of possibly transformed arguments. During recording/playback, the original arguments to the function call are passed through this transformer, and the transformed arguments are passed to arg-key-fn, recordable? and the recorded function. This can be useful for replacing an argument that would be destructively consumed (e.g. a mutable InputStream) with an indestructible substitute. The transformed arguments ought to be equivalent to the original arguments for the purpose of the code under test. The default is clojure.core/vector, which just passes along the original arguments.
  • :arg-key-fn: A function with the same argument signature as the recorded function, which returns a value for "fingerprinting" the arguments to each call. During recording, the value returned by this function will be saved along with the recorded call in the cassette. During playback, the value returned by this function will be used to look up a matching recorded call in the cassette. The default is clojure.core/vector, which just compares the arguments as given.
  • :recordable?: A function with the same argument signature as the recorded function, which returns truthy or falsy. If it returns falsy, the call will be passed through to the original function during both recording and playback. The default is clojure.core/identity.
  • :return-transformer: A single-argument function that takes a value returned by the recorded function and returns a transformed return value. During recording, the recorded function will be composed with this transformer function. This can be useful for ensuring serializability. The transformed return value ought to be equivalent to the original return value for the purpose of the code under test. The default is clojure.core/identity.

Cassette Customization

Instead of invoking with-cassette with a name, you may invoke it with a map defining additional cassette data:

  • :name: the only required key in the map, this defines the name of the cassette as previously described.
  • :serialization: an optional map defining settings for controlling how the cassette is serialized and deserialized.

De/serialization

vcr-clj uses Puget for storing cassettes on disk. The :serialization cassette key allows clients to customize the default configuration. The options available are:

  • :print-handlers: a function that takes precedence over the built-in function for converting the cassette output to serializable data. See Puget's documentation for more details
  • :data-readers: map that merges over the defaults. This mapping determines how specific symbols in the saved cassette are converted back to the original data.

The following example prints the raw bytes from a byte array instead of using the default base64 encoding.

(ns my.project-test
  (:require [clojure.test :refer :all]
            [puget.printer :as printer]
            [vcr-clj.cassettes.serialization :as vcr-ser]
            [vcr-clj.core :as vcr]))

(def byte-array-class
  "Standard Java class for byte arrays"
  (class (byte-array 0)))

(defn extended-print-handlers
  "Print handler for vcr-clj library. Enables support of additional object
  instances alongside vcr-clj defaults."
  [cls]
  (when (isa? cls byte-array-class)
    (printer/tagged-handler
      'my.project/printable-bytes
      (fn [data]
        (vcr-ser/split-bytes data 75)))))

(deftest here-is-my-bytes-test
  (with-cassette {:name :testaroo
                  :serialization {:print-handlers extended-print-handlers
                                  :data-readers {'my.project/printable-bytes (comp (fn [string] (.getBytes string))
                                                                                   vcr-ser/maybe-join)}}}
    ... do some testy things ...
    ... that will return byte arrays ...))

TODO

  • Add a better way to re-record than deleting cassette files. Maybe an environment variable?

License

Copyright (C) 2012 Gary Fredericks

Distributed under the Eclipse Public License, the same as Clojure.

More Repositories

1

quinedb

QuineDB is a quine that is also a key-value store.
Shell
580
star
2

test.chuck

A utility library for test.check
Clojure
215
star
3

debug-repl

A Clojure debug repl as nrepl middleware
Clojure
100
star
4

schpec

A utility library for clojure.spec
Clojure
45
star
5

exact

Portable exact arithmetic for Clojure[script]
Clojure
41
star
6

qubits

Qubits in Clojure
Clojure
40
star
7

seventy-one

71 in Clojure
Clojure
39
star
8

schema-bijections

Bijecting prismatic schemas
Clojure
36
star
9

how-to-ns

A Clojure linter for Stuart Sierra's how-to-ns standard
Clojure
33
star
10

catch-data

Clojure data-based exception handling
Clojure
32
star
11

clj-usage-graph

Haxy usage graphs for Clojure projects
Clojure
25
star
12

currj

Currying in Clojure
Clojure
20
star
13

dot-slash-2

Clojure library for creating (dev) namespaces of proxy vars
Clojure
17
star
14

user.clj

A library for loading clojure user files
Clojure
16
star
15

pg-serializability-bug

Reproduction code for Postgres BUG #13667
Shell
16
star
16

like-format-but-with-named-args

A function like clojure.core/format but with named args.
Clojure
13
star
17

dotfiles

dots and files
Shell
13
star
18

repl-utils

My repl utility things
Clojure
13
star
19

lein-lein

A collection of Leiningen plugins for doing all sorts of useful things
Clojure
13
star
20

the-halting-project

What's the simplest Turing Machine with unknown behavior?
Clojure
13
star
21

cljs-numbers

Hacking up some rich numerics for clojurescript
Clojure
12
star
22

lein-all-my-files-should-end-with-exactly-one-newline-character

A Clojure linter that enforces end-of-file newlines.
Clojure
11
star
23

clomp

A Stomp client library for Clojure.
Clojure
11
star
24

org-editor

Some clojure code for reading and writing org files
Clojure
8
star
25

corncob-cigar

Leiningen utilities.
Clojure
8
star
26

instant-coffee

Easy CoffeeScript without node.js
Clojure
7
star
27

webscale

Casual file-based persistence in Clojure.
Clojure
7
star
28

forty-two

Clojure number-to-words conversion
Clojure
7
star
29

persistent_js

Persistent Data Structures for JavaScript
Ruby
7
star
30

four

Random numbers in Clojure
Clojure
7
star
31

replog

Clojure repl history tracking
Clojure
6
star
32

bitalgs

Some code that visualizes hash functions
Clojure
6
star
33

vbox

A web-app front-end for VirtualBox
Ruby
5
star
34

lib-2367

Class generation utilities for Clojure
Clojure
5
star
35

dot-slash

Basic Leiningen plugin to setup a custom repl utils namespace
Clojure
5
star
36

4clojure-logic

The core.logic problems I assembled for a Chicago Clojure workshop.
Clojure
5
star
37

lib-5141

Simple customizable HTTP proxy in Clojure
Clojure
4
star
38

clojure-useless

A collection of utility functions that won't improve your Clojure code
Clojure
4
star
39

ranguages

Regular Language Manipulation in Clojure
Clojure
4
star
40

bijector

Bijections in Clojure
Clojure
4
star
41

compare

Helper functions for clojure.core/compare.
Clojure
4
star
42

misquote

Simple unquoting in Clojure
Clojure
4
star
43

test-check-workshop

Materials for my test.check workshop
Clojure
4
star
44

z

Simple complex numbers in Clojure
Clojure
3
star
45

eiPlog

event server
JavaScript
3
star
46

rubiks-cljube

Clojure
3
star
47

haystack

Dataset Difference Debugging in Clojure
Clojure
2
star
48

greengrocer

Web testing for Clojure
Clojure
2
star
49

tictactoe

It can't lose.
Clojure
2
star
50

2012-12-18

Support code for core.logic workshop
Clojure
2
star
51

git-git

Managing your swarm of git repositories.
Clojure
2
star
52

minus

Diffing/patching for Clojure data
Clojure
2
star
53

chess-clj

Messing around with chess and clojure
Clojure
2
star
54

referee

A library for handling exceptions across thread boundaries.
Clojure
1
star
55

lib-4642

This is not a project
Clojure
1
star
56

lein-env-version

Set leiningen project version with an environment variable.
Clojure
1
star
57

system-slash-exit

(System/exit n) in clojure
Clojure
1
star
58

engorge

Parsing/printing emacs org files in Clojure
Clojure
1
star
59

lib-7607

Casual Computational Combinatorics Framework
Clojure
1
star
60

lazy-prime-sieve

Clojure
1
star
61

bespoke-primes

The code behind http://gfredericks.com/sandbox/bespoke-primes
Clojure
1
star
62

ridge

A clojuresque lisp interpreted in Haskell
Haskell
1
star
63

cerealizer

Data serializationish-or-something libary for Ruby
Ruby
1
star
64

git-annex-utils

Some things I keep doing with git-annex
Clojure
1
star
65

data

My data
1
star
66

lib-4395

Clojure & algebra
Clojure
1
star
67

flexnum

Liberating numbers in ruby
Ruby
1
star