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

Imr*_*hid 15 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,但我很高兴在这里找到任何单元测试框架的解决方案.

Tra*_*own 11

正如我在上面的评论中指出的那样,基于Stefan Zeiger的解决方案,Shapeless 2.0(尚未发布,但目前作为里程碑提供)具有您正在寻找的功能的非常好的实现.我在这里为你的项目添加了一个演示(请注意,我必须更新到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())}
""")
Run Code Online (Sandbox Code Playgroud)

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

B.values().foreach {a => Union_B_C.getIdx(a) should be (a.ordinal())}
Run Code Online (Sandbox Code Playgroud)

我们得到以下编译错误:

[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
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢测试失败,那么您可以很容易地在Shapeless中调整实现.有关额外讨论,请参阅迈尔斯上一个问题的回答.


Dae*_*yth 6

Scalatest也可以做到这一点.

检查一段代码是否无法编译

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

"val a: String = 1" shouldNot compile
Run Code Online (Sandbox Code Playgroud)

如果要确保由于类型错误(而不是语法错误)而无法编译代码片段,请使用:

"val a: String = 1" shouldNot typeCheck
Run Code Online (Sandbox Code Playgroud)

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

如果你想声明一段代码确实编译,你可以用以下方法使其变得更加明显:

"val a: Int = 1" should compile
Run Code Online (Sandbox Code Playgroud)

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