Test Fixtures for RackUnit
This library defines fixtures, resources used in test cases that are automatically created and destroyed at the beginning and end of each test case. Fixtures are built on top of rackunit test cases and the disposable library; familiarity with the two is assumed in this document.
(define-fixture tmpdir (disposable-directory)) (define-fixture tmpfile (disposable-file)) (test-case/fixture "tests" #:fixture tmpdir #:fixture tmpfile (test-case "some-test" ... use (current-tmpdir) and (current-tmpfile) ...) (test-case "other-test" ... use different (current-tmpdir) and (current-tmpfile) ...))
Source code for this library is available on Github and is provided under the terms of the Apache License 2.0.
Warning! This library is experimental; it may change in backwards incompatible ways without notice. As such, now is the best time for feedback and suggestions so feel free to open a repository issue or reach out to me directly.
1 Overview of Collections and Modules
This package provides several modules, all in the fixture collection:
fixture - Re-provides the exports of fixture/base and fixture/rackunit.
fixture/base - Base definitions of fixtures and all testing framework agnostic forms.
fixture/rackunit - Tools for using fixtures with rackunit.
2 Data Model
(require fixture/base) | package: fixture |
A fixture is an external resource that must be properly initialized and disposed of for a test. Fixtures are essentially a pair of a disposable defining the external resource and a parameter that is set for each test to an instance of the disposable.
Additionally, fixtures may have fixture info; custom metadata about the current value of the fixture that can be used in test failure messages. Each fixture defines what info values it provides and there are no restrictions on the kind of values a fixture may use for info, although it’s expected that calling write on them produces something relatively useful.
> (define (example-info n) (format "example value of ~v" n))
> (define ex (fixture 'ex example-disposable #:info-proc example-info))
> (call/fixture ex (thunk (displayln (fixture-value ex)) (displayln (fixture-info ex))))
Allocated 10
10
example value of 10
Deallocated 10
> (fixture-value ex) fixture-value: contract violation
expected: fixture-initialized?
given: #<fixture>
in: an and/c case of
the 1st argument of
(->
(and/c fixture? fixture-initialized?)
any/c)
contract from: <pkgs>/fixture/base.rkt
blaming: program
(assuming the contract is correct)
at: <pkgs>/fixture/base.rkt:14:3
procedure
(fixture-initialized? fix) → boolean?
fix : fixture?
syntax
(define-fixture id disposable-expr fixture-option ...)
fixture-option = #:accessor-id accessor-id | #:info-proc info-proc-expr
disposable-expr : disposable?
info-proc-expr : (-> any/c any/c)
> (define (example-info n) (format "example value of ~v" n)) > (define-fixture ex example-disposable #:info-proc example-info)
> (call/fixture ex (thunk (displayln (current-ex)) (displayln (fixture-info ex))))
Allocated 60
60
example value of 60
Deallocated 60
> (current-ex) raise-argument-error: contract violation
expected: exact-nonnegative-integer?
given: "fixture"
procedure
(fixture-value fix) → any/c
fix : (and/c fixture? fixture-initialized?)
procedure
(call/fixture fix proc) → any
fix : fixture? proc : (-> any)
> (define-fixture ex example-disposable) > (call/fixture ex (thunk (* (current-ex) (current-ex))))
Allocated 5
Deallocated 5
25
procedure
(fixture-name fix) → symbol?
fix : fixture?
procedure
(fixture-info fix) → any/c
fix : (and/c fixture? fixture-initialized?)
> (struct example-info (value) #:transparent) > (define-fixture ex example-disposable #:info-proc example-info) > (call/fixture ex (thunk (fixture-info ex)))
Allocated 19
Deallocated 19
(example-info 19)
3 RackUnit Integration
(require fixture/rackunit) | package: fixture |
syntax
(test-begin/fixture fixture-clause ... body ...+)
fixture-clause = #:fixture fixture-expr
fixture-expr : fixture?
> (define-fixture ex1 example-disposable) > (define-fixture ex2 example-disposable) > (define (ex-sum) (+ (current-ex1) (current-ex2)))
> (test-begin/fixture #:fixture ex1 #:fixture ex2 (displayln (ex-sum)) (test-case "nested" (displayln (ex-sum))))
Allocated 19
Allocated 6
25
Allocated 71
Allocated 35
106
Deallocated 35
Deallocated 71
Deallocated 6
Deallocated 19
Additionally, test failures are augmented with a check-info with the name 'fixtures. The info’s value is a nested-info containing one check info for each fixture used; that info’s name and value correspond to the fixture’s name and its fixture info at the time the test failure occurred.
> (define-fixture file1 (disposable-file)) > (define-fixture file2 (disposable-file))
> (test-begin/fixture #:fixture file1 #:fixture file2 (check-equal? 1 2))
--------------------
FAILURE
fixtures:
file1: #<path:/var/tmp/rkttmp17214284811721428481883>
file2: #<path:/var/tmp/rkttmp17214284811721428481882>
name: check-equal?
location: eval:3:0
actual: 1
expected: 2
--------------------
syntax
(test-case/fixture name fixture-clause ... body ...+)
name = string-literal fixture-clause = #:fixture fixture-expr
fixture-expr : fixture?