2016-07-09 24 views
6

Ich habe diese bequeme Methode in meinen Tests:Wie kann ich Test-Utility-Methoden ignorieren, wenn scalatest Fehler erkennt?

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) 
    } 

, die für den Betrieb einer Behauptung Muster im Grunde eine Bequemlichkeit ist, dass ich viel zu tun.

Es ist nicht möglich, dies als Matcher neu zu schreiben wegen der impliziten Anforderung an SexpFormat[T] (obwohl ich beim Hören von Möglichkeiten interessiert sein würde, dies zu tun, die mich nicht benötigen den Typen MyFormat in foo should roundTrip[MyFormat](...) zu schreiben)

Wenn innerhalb dieser Utility-Methode Tests fehlschlagen, markiert scalatest die Interna von assertFormat als Ursache für den Testfehler. Aber ich möchte scalatest, um die Aufrufer dieser Methode zu erkennen, um die Ursache des Tests zu sein. Wie kann ich das machen?

d.h. Stromausgang

[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 ist, wo meine assertFormat definiert ist. Das eigentliche Versagen ist in FamilyFormatsSpec.scala:222

Antwort

2

Dies ist möglich, in ScalaTest 3.0, indem sie eine implizite org.scalactic.source.Position in Ihrer benutzerdefinierten Behauptung (die andere bequeme Methode FamilyFormatsSpec.scala:156 ruft). Die Position wird dann (über ein Makro) immer dann berechnet, wenn Ihre Methode assertFormat aufgerufen wird, und diese Position wird vom Ausdruck assert und matcher innerhalb von assertFormat übernommen. Hier ist, wie es aussehen würde:

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) 
} 

Das folgende Beispiel illstrates es. Wenn Sie ScalaTest 3.0 auf dem Klassenpfad haben, einfach: Laden Sie die folgende Datei in die 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") 

Es druckt:

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