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 75
75
example value of 75
Deallocated 75
> (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 66
66
example value of 66
Deallocated 66
> (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 61
Deallocated 61
3721
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 22
Deallocated 22
(example-info 22)
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 16
Allocated 64
80
Allocated 90
Allocated 25
115
Deallocated 25
Deallocated 90
Deallocated 64
Deallocated 16
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/rkttmp17474741121747474120819>
file2: #<path:/var/tmp/rkttmp17474741201747474120815>
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?