如何在scalatest检测到故障时忽略测试实用程序方法?

时间:2016-07-09 18:22:28

标签: scala scalatest

我的测试中有这种方便的方法:

  def assertFormat[T: SexpFormat](start: T, expect: Sexp): Unit = {
    val sexp = start.toSexp
    assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
    expect.convertTo[T] should be(start)
  }

这基本上是一种运行断言模式的便利,我做了很多。

由于对Matcher的隐含要求,因此无法将其重写为SexpFormat[T](尽管我有兴趣听到如何做到这一点,而不需要我写在MyFormat)中输入foo should roundTrip[MyFormat](...)

如果此实用程序方法中的任何测试失败,则scalatest会将assertFormat的内部标记为导致测试失败的原因。但我真的希望scalatest能够检测到此方法的调用者是测试的原因。我怎么能这样做?

即。当前输出是

[info] - should support custom missing value rules *** FAILED ***
[info]   SexpNil did not equal SexpCons(SexpSymbol(:duck),SexpCons(SexpNil,SexpNil)) nil was not (:duck nil) (FormatSpec.scala:11)
[info]   org.scalatest.exceptions.TestFailedException:
[info]   at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:529)
[info]   at org.scalatest.FlatSpec.newAssertionFailedException(FlatSpec.scala:1691)
[info]   at org.scalatest.Assertions$AssertionsHelper.macroAssert(Assertions.scala:502)
[info]   at org.ensime.sexp.formats.FormatSpec$class.assertFormat(FormatSpec.scala:11)
[info]   at org.ensime.sexp.formats.test.FamilyFormatsSpec.assertFormat(FamilyFormatsSpec.scala:151)
[info]   at org.ensime.sexp.formats.test.FamilyFormatsSpec.roundtrip(FamilyFormatsSpec.scala:156)
[info]   at org.ensime.sexp.formats.test.FamilyFormatsSpec$$anonfun$12.apply(FamilyFormatsSpec.scala:222)
[info]   at org.ensime.sexp.formats.test.FamilyFormatsSpec$$anonfun$12.apply(FamilyFormatsSpec.scala:221)

FormatSpec.scala:11是定义assertFormat的地方。真正的失败在FamilyFormatsSpec.scala:222(调用另一个便捷方法FamilyFormatsSpec.scala:156

1 个答案:

答案 0 :(得分:2)

这可以在ScalaTest 3.0中通过在自定义断言中使用隐式org.scalactic.source.Position来实现。无论何时调用assertFormat方法,都将计算位置(通过宏),并且assertFormat内的断言和匹配器表达式将获取该位置。以下是它的外观:

import org.scalactic.source

def assertFormat[T: SexpFormat](start: T, expect: Sexp)(implicit pos: source.Position): Unit = {
  val sexp = start.toSexp
  assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
  expect.convertTo[T] should be(start)
}

以下示例展示了它。如果您在类路径上有ScalaTest 3.0,只需:将以下文件加载到Scala REPL中:

:paste

import org.scalatest._
import org.scalactic._
import Matchers._

case class Sexp(o: Any) {
  def compactPrint: String = o.toString
  def convertTo[T: SexpFormat]: Sexp = implicitly[SexpFormat[T]].convertIt(o)
  override def toString = "I'm too sexp for my shirt."
}

trait SexpFormat[T] {
  def convertIt(o: Any): Sexp = new Sexp(o)
}

implicit class Sexpify(o: Any) {
  def toSexp: Sexp = new Sexp(o)
}

implicit def universalSexpFormat[T]: SexpFormat[T] = new SexpFormat[T] {}

def assertFormat[T: SexpFormat](start: T, expect: Sexp): Unit = {
  val sexp = start.toSexp
  assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
  expect.convertTo[T] should be(start)
}

import org.scalatest.exceptions.TestFailedException

val before = intercept[TestFailedException] { assertFormat(1, new Sexp) }

println(s"${before.failedCodeStackDepth} - This stack depth points to the assert call inside assertFormat")

import org.scalactic.source

def betterAssertFormat[T: SexpFormat](start: T, expect: Sexp)(implicit pos: source.Position): Unit = {
  val sexp = start.toSexp
  assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
  expect.convertTo[T] should be(start)
}

val after = intercept[TestFailedException] { betterAssertFormat(1, new Sexp) }

println(s"${after.failedCodeStackDepth} - This stack depth is the betterAssertFormat call itself in your test code")

它将打印:

3 - This stack depth points to the assert call inside assertFormat
4 - This stack depth is the betterAssertFormat call itself in your test code