如何编写确保compliation失败的scala单元测试?

时间:2013-11-21 06:51:37

标签: unit-testing scala

有没有办法编写像“单元测试”这样的东西来确保某些代码编译?

为什么我想要这样的东西?有两个原因。

1)检查我的API的类型安全性。我想要一种方法来确保是否有人传入错误值,您会收到编译器错误,而不仅仅是运行时错误。显然,我可以运行编译器并检查错误,但在单元测试中将其形式化是有利于避免回归&也用于文档。

例如,考虑这个测试。我用过一些注释掉的代码来检查类型安全性: https://github.com/squito/boxwood/blob/master/core/src/test/scala/com/quantifind/boxwood/EnumUnionTest.scala#L42 (第42行和第48行 - 在第34行,我调用了一个不同的API,它有一个运行时异常,我可以检查)

实际上我需要一段时间来获得类型安全权,所以这些都是重要的检查。现在,如果我去修改底层实现,我不能只运行我的测试套件 - 我还要记得取消注释这些行并检查编译器错误。

2)测试宏的错误处理。如果宏有一些错误的输入,它应该导致编译器错误。同样的问题,同样希望在易于运行的测试套件中使用它。

我使用ScalaTest,但我很高兴能在这里找到任何单元测试框架的解决方案。

3 个答案:

答案 0 :(得分:11)

正如我在上面的评论中指出的那样,基于Stefan Zeiger的解决方案,Shapeless 2.0(尚未发布但目前作为里程碑提供)具有您正在寻找的功能的非常好的实现。我已经为您的项目添加了一个演示here(请注意,我必须更新到Scala 2.10,因为此解决方案使用宏)。它的工作原理如下:

import shapeless.test.illTyped

//this version won't even compile
illTyped("getIdx(C.Ooga)")

//We can have multiple enum unions exist side by side
import Union_B_C._
B.values().foreach {b => Union_B_C.getIdx(b) should be (b.ordinal())}
C.values().foreach {c => Union_B_C.getIdx(c) should be (c.ordinal() + 2)}

//Though A exists in some union type, Union_B_C still doesn't know about it,
// so this won't compile
illTyped("""
  A.values().foreach {a => Union_B_C.getIdx(a) should be (a.ordinal())}
""")

如果我们要将第二次调用illTyped中的代码更改为将要编译的内容:

B.values().foreach {a => Union_B_C.getIdx(a) should be (a.ordinal())}

我们收到以下编译错误:

[error] .../EnumUnionTest.scala:56: Type-checking succeeded unexpectedly.
[error] Expected some error.
[error]     illTyped("""
[error]             ^
[error] one error found
[error] (core/test:compile) Compilation failed

如果你更喜欢测试失败,你很容易适应the implementation in Shapeless。有关额外讨论,请参阅Miles's answermy previous question

答案 1 :(得分:6)

Scalatest也可以这样做。

Checking that a snippet of code does not compile

  

通常在创建库时,您可能希望确保某些库   表示潜在“用户错误”的代码排列不会   编译,以便您的库更具错误性。 ScalaTest   Matchers trait包含以下语法:

"val a: String = 1" shouldNot compile
     

如果你想确保一个   代码片段由于类型错误而无法编译(相反   语法错误),使用:

"val a: String = 1" shouldNot typeCheck
     

请注意,shouldNot typeCheck语法只有在给定代码片段的情况下才会成功   由于类型错误而无法编译。仍会出现语法错误   抛出TestFailedException

     

如果你想声明一段代码确实编译,你可以做   更明显的是:

"val a: Int = 1" should compile
     

虽然前三个结构   用宏来实现,在编译时确定是否   由字符串表示的代码片段是否编译,   错误在运行时报告为测试失败。

答案 2 :(得分:0)

特拉维斯·布朗的回答绝对正确。为了完整起见,我想补充一点,这也适用于测试宏,如here所示。

一个小问题:illTyped检查在repl中似乎不起作用。它永远不会抛出错误,即使给定的表达式进行类型检查。但是,不要让那个愚弄你,它确实运作良好。

> test:console
Welcome to Scala version 2.10.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def foo(s: String) = s
foo: (s: String)String

scala> import shapeless.test.illTyped
import shapeless.test.illTyped

scala> foo(1)
<console>:10: error: type mismatch;
 found   : Int(1)
 required: String
              foo(1)
                  ^

scala> illTyped("""foo(1)""")

scala> illTyped("""foo("hi there")""")  // <--- works, but shouldn't!