【发布时间】:2016-07-25 19:24:57
【问题描述】:
我在 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)
}
}
}
当我测试阳性病例时,它按预期工作:
"return OK" in new WithApplication {
val response = route(FakeRequest(HttpVerbs.GET, "/test"))
import tools.MyMatchers._
response must haveHttpStatus(OK)
}
但是当我尝试测试一个否定的情况时,我得到一个编译错误,“value haveHttpStatus is not a member of 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)
}
我在一个示例 (https://gist.github.com/seratch/1414177) 中看到自定义匹配器被括在括号中。这行得通。把“不”放在最后也有效。
"return OK" in new WithApplication {
val response = route(FakeRequest(HttpVerbs.GET, "/test"))
import tools.MyMatchers._
response must not (haveHttpStatus(OK))
}
"also return OK" in new WithApplication {
val response = route(FakeRequest(HttpVerbs.GET, "/test"))
import tools.MyMatchers._
response must haveHttpStatus(OK) not
}
但我不太清楚为什么这两种方法有效,但最初的否定尝试却没有。如果有人可以对此有所了解,我真的很想了解每种方法的差异。这是在 Play Framework 2.4.6 项目中,包括 specs2 为 specs2 % Test。
查看返回的类型,我发现:
"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)
}
查看 AnyBeHaveMatchers,看起来我需要 haveHttpStatus 来返回 MatchResult,而不是 StatusMatcher,但我很难从这里到那里。
更新:
我钻取了 SizedCheckedMatcher,然后在 TraversableBaseMatchers 特征中用作
def haveSize[T : Sized](check: ValueCheck[Int]) = new SizedCheckedMatcher[T](check, "size")
然后在TraversableBeHaveMatchers里面,有HasSize这个类,调用的时候会返回一个MatchResult
def size(n: Int) : MatchResult[T] = s(outer.haveSize[T](n))
这与https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/LogicalMatcherSpec.scala 中的 CustomMatcher 示例几乎相同。
我在尝试复制时遇到的问题是,在调用 s() 或 result() 时,我得到了编译错误
无法在 org.specs2.matcher.MatchResult[Option[scala.concurrent.Future[play.api.mvc.Result]]] 中访问 trait MatchResult 中应用的方法]
【问题讨论】:
-
暂且不提你的问题,根据这个playframework.com/documentation/2.4.x/ScalaTestingWithSpecs2,你可以使用内置的
status(result) mustEqual OK -
我们最初使用了这种方法。当
response must beSome.which(status(_) == NOT_FOUND)失败时,我们会收到类似'Some(scala.concurrent.impl.Promise$DefaultPromise@14fb07de)' is Some but the function returns 'false' on 'scala.concurrent.impl.Promise$DefaultPromise@14fb07de'的错误消息。我们沿着创建自定义匹配器的路线走,这样如果我们的 Jenkins 作业中的测试失败,我们会在控制台输出中收到一条消息,例如'Some(scala.concurrent.impl.Promise$DefaultPromise@4e37d15b)' expected 404 but found 200,这样我们就可以更快地解决问题。 -
尝试在你的 ide 中导航到
not的来源,当你不使用括号时,它与使用它们时是不同的。当你这样做时,它是一个简单的匹配器,它否定你给出的那个,而当你不这样做时,它更像是这样List(1, 3).must(not).contain(1)所以must被应用def must(m: =>Matcher[T]) = applyMatcher(m)给MatchResult[T],稍后会隐式转换到具有def contain(check: ValueCheck[T]) = s(outer.contain(check))方法的TraversableBeHaveMatchers[T]。您只需浏览来源即可找到所有这些内容。 -
这很有帮助 - 我在上面添加了一些额外的细节。看来我此时唯一的挂断是无法为 MatchResult 调用 apply 函数。
-
你的答案还可以吗?
标签: scala playframework specs2