akka - Why are implicit variables not initialized in Scala when called from unit test? -
given the following singleton object in 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") { { complete { future.successful("done") } } } } val bindingfuture = http().bindandhandle(route, "localhost", 8080) }
and following unit test
package demo import akka.http.scaladsl.testkit.scalatestroutetest import org.scalactic.typecheckedtripleequals import org.scalatest.{inspectors, matchers, wordspec} class webserverspec extends wordspec matchers typecheckedtripleequals inspectors 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" } } } }
i following error:
[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 @ akka.http.scaladsl.server.directives.executiondirectives$$anonfun$handleexceptions$1$$anonfun$apply$1.apply(executiondirectives.scala:33) @ akka.http.scaladsl.server.directives.executiondirectives$$anonfun$handleexceptions$1$$anonfun$apply$1.apply(executiondirectives.scala:29) @ akka.http.scaladsl.testkit.routetest$tildearrow$$anon$1.apply(routetest.scala:162) @ akka.http.scaladsl.testkit.routetest$tildearrow$$anon$1.apply(routetest.scala:150)
it took me while figure out. thing is: removing extends app
make test succeed.
the reason problem when webserver
declared extends app
, uses delayedinit
functionality of app
trait. because of this, initialization code in contructor not added constructor of webserver object. instead called when main
method called on webserver. when references "route" inside tests, coming null.
mixing in delayedinit
trait (app
extends delayedinit
) rewrite class or object template. instead of adding val's , var's constructor, added def delayedinit(body: => unit)
hook (inaccessible user code). apparently 1 called whenever main method called.
you can verify calling "main" on webserver inside test. if this, test pass. because calling main triggers initialization resulting in objects being created.
generally speaking though right solution move routing somewhere else, rather having inside of base app.
Comments
Post a Comment