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 44
44
example value of 44
Deallocated 44
> (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 49
49
example value of 49
Deallocated 49
> (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 41
Deallocated 41
1681
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 35
Deallocated 35
(example-info 35)
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 25
Allocated 24
49
Allocated 90
Allocated 58
148
Deallocated 58
Deallocated 90
Deallocated 24
Deallocated 25
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/rkttmp17417141301741714130037>
file2: #<path:/var/tmp/rkttmp17417141301741714130036>
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?