【问题标题】:Unit testing macros in racket球拍中的单元测试宏
【发布时间】:2018-07-05 16:42:43
【问题描述】:

我目前正在努力通过“漂亮的球拍”,尝试编写单元测试。

对宏进行单元测试的最佳方法是什么?例如,如果我有一个宏 infix:

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

测试模式匹配和转换的最明智的方法是什么?我想做类似的事情:

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

【问题讨论】:

  • 我知道有两种策略。 (1) 测试它作为输出生成的代码的行为,而不是输出本身。这看起来像(check-equal? (infix [3 - 2]) 1)。 (2) 对宏的大部分复杂度使用辅助函数,并测试辅助函数。这看起来像 (define-macro (infix infix-expr) (process-infix #'infix-expr)),您可以在其中编写像 (begin-for-syntax (check-stx-equal? (process-infix #'[3 - 2]) #'(- 3 2))) 这样的测试。
  • 只是出于兴趣,我对 (2) 进行了快速操作,但无法得到任何合理的工作 - 我在定义宏时收到 process-infix: unbound identifier 错误。
  • 您是否将process-infix 的定义放在begin-for-syntax 块内?查看Calling other functions in macros 的答案,了解如何操作。

标签: unit-testing racket


【解决方案1】:

通过测试宏的扩展来对宏进行单元测试几乎总是不是您想要的。这有点像用模拟测试所有东西——你最终会得到很多测试,这些测试过于耦合到某些东西的实现,甚至不一定能保证它的行为

因此,当您测试一个宏时,您几乎总是只想通过验证它确实做了正确的事情来测试它,而不是它扩展成什么。对于你的宏,我会写一些这样的测试用例:

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

对于做更复杂事情的宏,我仍然建议不要对扩展进行断言。如果必须,请在此处使用现有的单元测试工具包。即使在测试宏时,同样的原则也适用:使用依赖注入来替换难以测试的东西,如果需要,为单元测试提供稍微低级的接口,与协作者不紧密耦合。

如果您确实觉得需要更细粒度的单元测试,来自syntax/macro-testingphase1-eval 可能会有所帮助,因为它允许您在编译时评估您在测试套件中定义的函数。也就是说,我敦促您尽可能少地这样做。在我使用 Racket 期间,我编写了一些非常密集的宏,并且我设法在没有查看它们的扩展的情况下对它们进行了行为测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-09
    • 2012-01-07
    • 2012-02-27
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-03
    相关资源
    最近更新 更多