为什么从单元测试调用时隐式变量没有在 Scala 中初始化?
Why are implicit variables not initialized in Scala when called from unit test?
给定 Scala 中的以下单例对象:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
以及下面的单元测试
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
我收到以下错误:
[ERROR] [04/19/2016 07:12:18.995]
[ScalaTest-run-running-WebServerSpec]
[akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during
processing of request
HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))
java.lang.NullPointerException at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$$anonfun$apply.apply(ExecutionDirectives.scala:33)
at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$$anonfun$apply.apply(ExecutionDirectives.scala:29)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon.apply(RouteTest.scala:162)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon.apply(RouteTest.scala:150)
我花了一段时间才弄明白。问题是:删除 extends app
将使测试成功。
问题的原因是当 WebServer
声明为 extends App
时,它使用了 App
特性的 DelayedInit
功能。因此,构造函数中的初始化代码 而不是 添加到 WebServer 对象的构造函数中。相反,在 WebServer 上调用 main
方法时调用。因此,当他在测试中引用 "route" 时,这些都是空的。
混合 DelayedInit
特征(App
从 DelayedInit
扩展)将重写您的 class 或对象模板。它不会将您的 val 和 var 添加到构造函数中,而是将其添加到 def delayedInit(body: => Unit)
挂钩(用户代码无法访问)。显然,每当调用 main 方法时都会调用此方法。
您可以通过简单地在测试中的 WebServer 上调用 "main" 来验证这一点。如果你这样做,那么测试就会通过。这是因为调用 main 会触发初始化,从而创建这些对象。
一般来说,正确的解决方案可能是将路由移动到其他地方,而不是将其放在基础应用程序中。
给定 Scala 中的以下单例对象:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
以及下面的单元测试
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
我收到以下错误:
[ERROR] [04/19/2016 07:12:18.995] [ScalaTest-run-running-WebServerSpec] [akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during processing of request HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)) java.lang.NullPointerException at akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$$anonfun$apply.apply(ExecutionDirectives.scala:33) at akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$$anonfun$apply.apply(ExecutionDirectives.scala:29) at akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon.apply(RouteTest.scala:162) at akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon.apply(RouteTest.scala:150)
我花了一段时间才弄明白。问题是:删除 extends app
将使测试成功。
问题的原因是当 WebServer
声明为 extends App
时,它使用了 App
特性的 DelayedInit
功能。因此,构造函数中的初始化代码 而不是 添加到 WebServer 对象的构造函数中。相反,在 WebServer 上调用 main
方法时调用。因此,当他在测试中引用 "route" 时,这些都是空的。
混合 DelayedInit
特征(App
从 DelayedInit
扩展)将重写您的 class 或对象模板。它不会将您的 val 和 var 添加到构造函数中,而是将其添加到 def delayedInit(body: => Unit)
挂钩(用户代码无法访问)。显然,每当调用 main 方法时都会调用此方法。
您可以通过简单地在测试中的 WebServer 上调用 "main" 来验证这一点。如果你这样做,那么测试就会通过。这是因为调用 main 会触发初始化,从而创建这些对象。
一般来说,正确的解决方案可能是将路由移动到其他地方,而不是将其放在基础应用程序中。