2016-04-05 9 views
0

ich einen benutzerdefinierten Matcher in specs2 geschrieben hatte, wie folgt:Negieren einen benutzerdefinierten Matcher in specs2

object MyMatchers { 
    def haveHttpStatus(expected:Int) = new StatusMatcher(expected) 
} 

class StatusMatcher(expected:Int) extends Matcher[Option[Future[Result]]] { 

    def apply[R <: Option[Future[Result]]](r: Expectable[R]) = { 
    val v = r.value 
    v match { 
     case None => failure(s"${r.description} was None", r) 
     case Some(fr:Future[Result]) => 
     import play.api.test.Helpers._ 
     val actual:Int = status(fr) 
     result(actual == expected, 
      s"${r.description} has status $actual as expected", 
      s"${r.description} expected status $expected but found $actual", 
      r) 
     case _ => 
     failure(s"${r.description} has unexpected type $v", r) 
    } 
    } 
} 

Als ich für den positiven Fall zu testen, funktioniert es wie erwartet:

"return OK" in new WithApplication { 
     val response = route(FakeRequest(HttpVerbs.GET, "/test")) 
     import tools.MyMatchers._ 
     response must haveHttpStatus(OK) 
    } 

Aber als ich versuche, einen negativen Fall zu testen, bekomme ich einen Kompilierfehler, "Wert haveHttpStatus ist kein Mitglied von org.specs2.matcher.MatchResult [Option [scala.concurrent.Future [play.api.mvc.Result]]"

"return OK" in new WithApplication { 
     val response = route(FakeRequest(HttpVerbs.GET, "/test")) 
     import tools.MyMatchers._ 
     response must not haveHttpStatus(OK) 
    } 

Ich sah in einem Beispiel (https://gist.github.com/seratch/1414177), wo der benutzerdefinierte Matcher in Klammern gewickelt wurde. Das hat funktioniert. Das "Nicht" am Ende zu setzen, hat auch funktioniert.

Aber ich bin nicht wirklich klar, warum diese beiden Ansätze funktionieren, aber der ursprüngliche Versuch der Verneinung nicht. Wenn jemand etwas Licht ins Dunkel bringen kann, würde ich gerne die Unterschiede in jedem Ansatz verstehen. Dies ist in einem Play Framework 2.4.6-Projekt, einschließlich specs2 als specs2 % Test.

auf die Arten suchen zurückgekehrt, und ich fand:

"return OK" in new WithApplication { 
    val response = route(FakeRequest(HttpVerbs.GET, "/test")) 
    import tools.MyMatchers._ 
    val matcher1 = haveHttpStatus(OK)  // <-- is type StatusMatcher 
    val matcher2 = (haveHttpStatus(OK))  // <-- is type StatusMatcher 
    val matcher3 = not (haveHttpStatus(OK)) // <-- is type AnyRef with Matcher[Option[Future[Result]]] 
    val matcher4 = not haveHttpStatus(OK) // <-- doesn't compile - gives the error "value haveHttpStatus is not a member of org.specs2.matcher.NotMatcher[Any]" 

    response must haveHttpStatus(OK) 
} 

durch AnyBeHaveMatchers Sehen, es sieht aus wie ich haveHttpStatus brauchen eine MatchResult zurückzukehren, anstatt eine StatusMatcher, aber ich habe eine harte Zeit von hier nach dort kommen.

Update:

ich durch SizedCheckedMatcher gebohrt, die dann in der TraversableBaseMatchers Eigenschaft als

def haveSize[T : Sized](check: ValueCheck[Int]) = new SizedCheckedMatcher[T](check, "size") 

Dann in TraversableBeHaveMatchers verwendet wird, gibt es die Klasse HasSize, die eine MatchResult zurückgibt, wenn Sie

rufen
def size(n: Int) : MatchResult[T] = s(outer.haveSize[T](n)) 

Das ist ziemlich genau dasselbe wie das CustomMatcher-Beispiel in https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/LogicalMatcherSpec.scala.

Die Frage, die ich schlagen bin versucht, entweder zu replizieren ist, dass, wenn s() aufrufen oder Ergebnis(), ich die Compiler-Fehler erhalten

Methode in Zug MatchResult anwenden können nicht in org.specs2 zugegriffen werden. matcher.MatchResult [Option [scala.concurrent.Future [play.api.mvc.Result]]]

+0

Art beiseite zu Ihrer Frage nach diesem https://www.playframework.com/documentation /2.4.x/ScalaTestingWithSpecs2 können Sie eingebaute 'Status (Ergebnis) verwenden mustEqual OK' –

+0

Wir verwendeten ursprünglich diesen Ansatz. Wenn "Antwort mussSome sein.was (status (_) == NOT_FOUND) 'gescheitert ist, erhalten wir eine Fehlermeldung wie' 'Einige ([email protected])' ist einige, aber die Funktion gibt 'false' auf 'scala zurück. [email protected] ''. Wir gingen den Weg der Erstellung eines benutzerdefinierten Matcher, so dass, wenn ein Test in unserem Jenkins-Job fehlschlagen würde, wir eine Nachricht in der Konsolenausgabe wie "Einige ([email protected])" erwarten 404 aber 200 gefunden, damit wir das Problem schneller auflösen konnten. – bhnat

+1

Versuchen Sie, in Ihrer IDE zur Quelle von 'not' zu navigieren, wenn Sie keine parens verwenden, ist es eine andere Sache als wenn Sie sie verwenden. Wenn du es tust, ist es ein einfacher Matcher, der den, den du gibst, negiert, während wenn du es nicht tust, ist es eher so "Liste (1, 3) .muss (nicht) .enthalten (1)" so "muss" ist applied 'def muss (m: => Matcher [T]) = applyMatcher (m)' ergibt 'MatchResult [T]', das später implizit in 'TraversableBeHaveMatchers [T]' umgewandelt wird, das die Methode 'def contain (check : ValueCheck [T]) = s (outer.contain (check)) '. Sie können all dies nur durch Quellen navigieren. –

Antwort

0

Wenn Sie eine Syntax mit a must not beOk[A] verwenden möchten, wo beOk ein Brauch ist Matcher Sie eine implizite Konvertierung zur Verfügung stellen müssen (ein Beispiel here):

Und übrigens eine einfachere Art und Weise individuellen Matcher zu erstellen, ist implizite Konvertierungen zu verwenden:

import org.specs2.matcher.MatcherImplicits._ 

type Res = Option[Future[Result]] 

def haveHttpStatus(expectedStatus: Int): Matcher[Res] = { actual: Res => 
    actual match { 
    case None => 
     (false, s"the result was None") 

    case Some(fr:Future[Result]) => 
     import play.api.test.Helpers._ 
     val actualStatus = status(fr) 
     (actualStatus == expectedStatus, 
     s"expected status $expectedStatus but found $actualStatus") 

    case v => 
     (false, s"unexpected type ${v.getClass}") 
    } 
} 
+0

Ich werde versuchen, dies mit einem anderen benutzerdefinierten Matcher herumspielen. Momentan bekomme ich "value haveHttpStatus ist kein Mitglied von org.specs2.matcher.MatchResult [Option [scala.concurrent.Future [play.api.mvc.Result]]]" wenn ich versuche "muss nichtHttpStatus (200) haben ". Aber kein Fehler ohne das "nicht". – bhnat

+0

Haben Sie die 'implizite Klasse NotStatusMatcherMatcher' im Bereich? Denn diese sollte dir die 'haveHttpStatus'-Methode geben. – Eric

+0

Ich habe dies zu der play-scala-intro Vorlage für ein Beispiel hinzugefügt - https://github.com/bhnat/play-scala-intro. In tools/Matchers.scala habe ich 'trait NotHttpMatcher { implizite Klasse NotStatusMatcherMatcher (Ergebnis: NotMatcher [Option [Zukunft [Ergebnis]]]) { def haveHttpStatus (erwartet: Int) = MyMatchers.haveHttpStatus (erwartet) .not } } 'und dann dieses Merkmal in ApplicationSpec erweitert. – bhnat