Allow definitions in `#:before` for `test-suite`
kstrafe opened this issue · comments
In the tests for rkt-glfw we need to set up a window and that window is stored as a variable. Right now I use a set!
but this feels dirty (hey, it's mutation). So it would be really cool if there's something like #:literal-before
that could paste the code right before each test-case. Here's what I'm looking for:
(define-test-suite window-functions
#:literal-before (begin (glfwInit) (define window (glfwCreateWindow 400 300 "" #f #f)))
;; Literal-after can only reference identifiers from literal-before, so tests can't overwrite by accident
#:literal-after (begin (glfwDestroyWindow window) (glfwTerminate))
...)
To clarify, the expectation is that this:
(test-suite "some-test-suite"
#:literal-before (begin before-body ...)
#:literal-after (begin after-body ...)
test-body ...)
...would be roughly equivalent to this:
(test-suite "some-test-suite"
(begin (begin before-body ...
test-body)
after-body ...)
...)
Is that correct?
Assuming that's what you're looking for, I'm not sure this is the best approach. It's heavy on macros in a way that can create confusing scopes and error messages, and it might encourage a lot of copy-pasting of before and after code.
You might be interested in my fixture
package which provides a way to abstract over this sort of test resource setup and teardown code. Using fixtures, I think the tests you linked to would look like this:
(define (create-test-window)
(glfwInit)
(glfwCreateWindow 400 300 "Main window" #f #f))
(define (destroy-test-window window)
(glfwDestroyWindow window)
(glfwTerminate))
(define-fixture test-window (disposable create-test-window destroy-test-window))
(test-case/fixture window-functions
#:fixture test-window
;; now everybody gets their own window via (current-test-window)
(test-case "Setting and getting window size succeeds"
(glfwSetWindowSize (current-test-window) 350 250)
(sleep 1) ; There is a slight delay before the size is actually set
(let-values ([(x y) (glfwGetWindowSize (current-test-window))])
(check-equal? x 350)
(check-equal? y 250)))
(test-case "Setting window position succeeds"
(glfwSetWindowPos (current-test-window) 10 10))
(test-case "Setting window title succeeds"
(glfwSetWindowTitle (current-test-window) "Test title"))
(test-case "Window iconify and restore succeeds"
(glfwIconifyWindow (current-test-window))
(glfwRestoreWindow (current-test-window))))
The test-case/fixture
form specifies that the test and each nested test should all get their own instance of each of the fixtures listed in the #:fixture
clauses. You can included nested uses of test-case/fixture
to specify that some nested tests get additional fixtures. Also, the current value of each fixture is automatically shown in the test failure output. Would this sort of thing work for you?
This looks good. If possible there should be a link/mention to/of fixture for the before/after and #:before/#:after documentation. I didn't know about this at all. And additionally make it rackunit/fixture.
Doc links are a good idea. There's a few other community packages (as in, not "main distribution" packages) that extend RackUnit in various ways; a section in the docs mentioning them would be a helpful addition. I'll file an issue.
As for rackunit/fixture
naming: the fixture
library's core API is available as fixture
with the RackUnit-specific bits available as fixture/rackunit
. The core API is meant to be usable by other test frameworks, hence the standalone name.