简体   繁体   中英

Play 2.4 macwire example with extended controllers doesn't seem like it can be mocked

I took the existing macwire example and extended the controller like so CoffeeController.scala

package com.softwaremill.play24.controllers

import com.softwaremill.play24.dao.CoffeeDao
import play.api.i18n.Lang
import play.api.libs.json.Json
import play.api.mvc._

import scala.concurrent.{Future}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.mvc.Results._

class CoffeeController(
  coffeeDao: CoffeeDao
)(implicit ec: SomeContextBuilder) extends AnotherController {

  def fetchAll() = DecoratedAction() { request =>
    coffeeDao.all.map { coffees =>
      Ok(Json.toJson(coffees))
    }
  }

  def priced(price: Double) = ResolvedDecoratedAction() { request =>
    coffeeDao.byPriceWithSuppliers(price).map { result =>
      Ok(Json.toJson(result.toMap))
    }
  }
}

trait ContextBuilder[U <: TraitLike] {
  def build(request: Request[AnyContent]): Future[Either[Result, RequestWithContext[U]]]
  def buildAuthenticated(request: Request[AnyContent]): Future[Either[Result, RequestWithResolvedContext[U]]]
}

trait TraitLike {
  def id: String
}

trait WithSessionId {
  self: RequestHeader =>
  lazy val sessionId = self.session.get("auth").getOrElse(java.util.UUID.randomUUID().toString)
}

case class RequestWithContext[U <: TraitLike](request: Request[AnyContent], lang: Lang, anything: Option[U]) extends WrappedRequest(request) with WithSessionId
case class RequestWithResolvedContext[U <: TraitLike](request: Request[AnyContent], lang: Lang, anything: U, rememberMe: Boolean = false) extends WrappedRequest(request) with WithSessionId
case class Trait(val id: String) extends TraitLike

class AnotherController[U <: TraitLike](implicit ctxBuilder: ContextBuilder[U]) extends Controller  {
  def DecoratedAction(bodyParser: BodyParser[AnyContent] = parse.anyContent)(f: RequestWithContext[U] => Future[Result]) = Action.async {
    implicit request =>
      ctxBuilder.build(request) flatMap {
        case Left(r) =>
          Future.successful(r)
        case Right(requestContext) =>
          f(requestContext).map(_.addingToSession( ("auth" , requestContext.sessionId) ))
      }
  }

  def ResolvedDecoratedAction(bodyParser: BodyParser[AnyContent] = parse.anyContent)(f: RequestWithResolvedContext[U] => Future[Result]) = Action.async {
    implicit request =>
      ctxBuilder.buildAuthenticated(request) flatMap {
        case Left(r) =>
          Future.successful(r)
        case Right(requestContext) =>
          f(requestContext).map(_.addingToSession( ("auth", requestContext.sessionId) ))
      }
  }
}

class SomeContextBuilder extends ContextBuilder[TraitLike] {
  override def build(request: Request[AnyContent]): Future[Either[Result, RequestWithContext[TraitLike]]] = Future.successful(Right(RequestWithContext(request, Lang("en-us"),None)))

  override def buildAuthenticated(request: Request[AnyContent]): Future[Either[Result, RequestWithResolvedContext[TraitLike]]] = Future.successful(Right(RequestWithResolvedContext(request, Lang("en-us"),Trait("id"),false)))
}

ControllerModule.scala

package com.softwaremill.play24.modules

import com.softwaremill.macwire._
import com.softwaremill.play24.controllers.{SomeContextBuilder, SupplierController, CoffeeController}
import com.softwaremill.play24.dao.{CoffeeDao, SupplierDao}
import play.api.libs.ws.WSClient

import scala.concurrent.ExecutionContext

trait ControllerModule {

  // Dependencies
  implicit def ec: ExecutionContext
  implicit val ctxBuilder = wire[SomeContextBuilder]
  def wsClient: WSClient
  def supplierDao: SupplierDao
  def coffeeDao: CoffeeDao

  // Controllers
  lazy val supplierController = wire[SupplierController]
  lazy val coffeeController = wire[CoffeeController]
}

However when I run the tests I'm getting a stacktrace:

[error]   ! return priced by coffee, supplier
  [error]    No configuration setting found for key 'play.crypto.secret' (SimpleConfig.java:152)
[error] com.typesafe.config.impl.SimpleConfig.findKeyOrNull(SimpleConfig.java:152)
[error] com.typesafe.config.impl.SimpleConfig.findOrNull(SimpleConfig.java:170)
[error] com.typesafe.config.impl.SimpleConfig.findOrNull(SimpleConfig.java:176)
[error] com.typesafe.config.impl.SimpleConfig.findOrNull(SimpleConfig.java:176)
[error] com.typesafe.config.impl.SimpleConfig.findOrNull(SimpleConfig.java:193)
[error] com.typesafe.config.impl.SimpleConfig.findOrNull(SimpleConfig.java:198)
[error] com.typesafe.config.impl.SimpleConfig.getIsNull(SimpleConfig.java:208)
[error] play.api.PlayConfig.getOptional(Configuration.scala:951)
[error] play.api.PlayConfig.getOptionalDeprecated(Configuration.scala:996)
[error] play.api.libs.CryptoConfigParser.get$lzycompute(Crypto.scala:232)
[error] play.api.libs.CryptoConfigParser.get(Crypto.scala:203)
[error] play.api.libs.Crypto$$anonfun$crypto$1.apply(Crypto.scala:42)
[error] play.api.libs.Crypto$$anonfun$crypto$1.apply(Crypto.scala:40)
[error] play.api.libs.Crypto$.crypto(Crypto.scala:43)
[error] play.api.libs.Crypto$.sign(Crypto.scala:67)
[error] play.api.mvc.CookieBaker$class.encode(Http.scala:502)
[error] play.api.mvc.Session$.encode(Http.scala:651)
[error] play.api.mvc.CookieBaker$class.encodeAsCookie(Http.scala:554)
[error] play.api.mvc.Session$.encodeAsCookie(Http.scala:651)
[error] play.api.mvc.Result.withSession(Results.scala:170)
[error] play.api.mvc.Result.addingToSession(Results.scala:262)
[error] com.softwaremill.play24.controllers.AnotherController$$anonfun$ResolvedDecoratedAction$1$$anonfun$apply$5$$anonfun$apply$6.apply(CoffeeController.scala:64)
[error] com.softwaremill.play24.controllers.AnotherController$$anonfun$ResolvedDecoratedAction$1$$anonfun$apply$5$$anonfun$apply$6.apply(CoffeeController.scala:64)

I could try running(FakeApplication()){...} but then I get routes injector errors. Currently the code uses routesGenerator := InjectedRoutesGenerator in the build.sbt, but I don't think it translates over into test phase.

I have put the code up on here https://github.com/tashiscool/Play24MacwireMockingFailure

If you run activator test you should see the errors in the controllers tests.

i ran into the exact same problem. i spent a little bit of time looking into it, it appears that via FakeRequest#withSession , you can end up calling some parts of the Crypto library within play that depend on config but have no way to pass in a FakeApplication .

...but i did find a simple workaround that might work for you as well. basically just wrapping FakeRequest with a mockito spy, and then stubbing the methods that try to call the internal Crypto API.

import org.mockito.Mockito.{doReturn, spy}
import play.api.mvc.{AnyContentAsEmpty, Session}
import play.api.test.FakeRequest

trait AuthSupport { 

  def fakeRequest(): FakeRequest[AnyContentAsEmpty.type] = {
    val request = spy(FakeRequest())
    doReturn(Session(Map("userId" -> "1"))).when(request).session
    request
  }

}

Looks like you didn't configured play secret in application.conf
Can you try and define it?

Look here for more details

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM