我正在使用Scala.js,并编写了一个为JVM和JS实现的特性。我使用第三方JVM和JS库在双方实现它,应该在JVM和浏览器中提供功能相同的结果。但是,我需要编写一个测试来验证它!
如果我只是测试两个vanilla Scala实现,我就知道该怎么做了。我写了特征输入的生成器,并从中驱动每个函数,比较每个函数的结果。 (我可以假设函数结果是布尔,整数,长整数,字符串,相同的集合,或者可能是toString()' d。
有人在那里做这种测试吗?
如果一个实现是在Javascript中,我该如何做到这一点?幻影? (我可以将生成的JS文件传递给它,而不是简单的JS-as-strings吗?)还有其他什么?
答案 0 :(得分:2)
您可以在宏中使用Scala的reflective toolbox来在编译时(在JVM上)执行测试代码。然后,您可以使用结果并生成用于比较值的代码。
所以我们想写一个宏,给出以下代码:
FuncTest.test { (1.0).toString }
可以生成这样的内容:
assert("1.0" == (1.0).toString)
这实际上听起来比现在更难。让我们从FuncTest
的宏骨架开始:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object FuncTest {
def test[T](x: => T): Unit = macro FuncTestImpl.impl[T]
}
class FuncTestImpl(val c: Context) {
import c.universe._
def impl[T : WeakTypeTag](x: Tree): Tree = ???
}
在impl
内部,我们想在x
中运行代码,然后生成一个断言(或者适合您使用的测试框架):
import scala.reflect.runtime.{universe => ru}
import scala.tools.reflect.ToolBox
def impl[T : WeakTypeTag](x: Tree): Tree = {
// Make a tool box (runtime compiler and evaluater)
val mirror = ru.runtimeMirror(getClass.getClassLoader)
val toolBox = mirror.mkToolBox()
// Import trees from compile time to runtime universe
val importer = ru.mkImporter(c.universe)
val tree = toolBox.untypecheck(importer.importTree(x))
// Evaluate expression and make a literal tree
val result = toolBox.eval(tree)
val resultTree = reifyLiteral(result)
// Emit assertion
q"assert($x == $resultTree)"
}
我们遇到的唯一问题是reifyLiteral
。它基本上应该采用任意值并从中创建一个文字。一般来说这很难/不可能。但是,对于某些基本值(基元,字符串等)来说非常容易:
/** Creates a literal tree out of a value (if possible) */
private def reifyLiteral(x: Any): Tree = x match {
case x: Int => q"$x"
case x: String => q"$x"
// Example for Seq
case x: Seq[_] =>
val elems = x.map(reifyLiteral)
q"Seq(..$elems)"
case _ =>
c.abort(c.enclosingPosition, s"Cannot reify $x of type ${x.getClass}")
}
就是这样。你现在可以写:
FuncTest.test { /* your code */ }
自动生成计算库的测试。
警告此工具箱目前没有注入正确的类路径。因此,如果您使用外部库(我假设您这样做),您也需要调整它。如果您需要帮助,请告诉我。