leontrolski
leontrolski

Reputation: 385

Unit testing macros in racket

I'm currently working my way through "beautiful racket", trying to write unit tests as I go.

What is the best way to unit test macros? for example, if I have a macro infix:

(define-macro (infix [A B C]) #'(B A C))

what is the most sensible way to test the pattern matching, and the transformation? I'd like to do something like:

(check equal? (infix '(3 - 2)) '(- 3 2))

Upvotes: 3

Views: 215

Answers (1)

Alexis King
Alexis King

Reputation: 43842

Unit testing macros by testing their expansion is almost always not what you want. It’s a little bit like testing everything with mocks—you end up with a lot of tests far too coupled to the implementation of something that doesn’t even necessarily guarantee its behavior.

Therefore, when you’re testing a macro, you almost always just want to test it by verifying it actually does the right thing, not what it expands to. In the case of your macro, I’d just write some test cases like this:

(check-equal? (infix (3 - 2)) 1)
(check-equal? (infix (4 / 2)) 2)

For macros that do more complicated things, I would still recommend against making assertions about the expansion. If you have to, use your existing unit-testing toolkit here. Even when testing macros, the same principles apply: use dependency injection to replace things that are difficult to test, and if you need to, provide a slightly lower level interface for your unit tests that isn’t as tightly coupled to its collaborators.

In the case that you really do feel like you need more fine-grained unit testing, phase1-eval from syntax/macro-testing can be helpful, since it allows you to evaluate functions you define at compile-time in your test suite. That said, I’d urge you to do this as little as possible. I’ve written some pretty intense macros during my time using Racket, and I’ve managed to test them all behaviorally without looking at their expansion.

Upvotes: 6

Related Questions