2

I would like to write a simple test for a controller, which accepts a json body. But as soon I add the parse.json BodyParser to the Action my Tests cannot be compiled anymore. The Setup is basically the plain play-scala-seed project.

Error:

[error] ... could not find implicit value for parameter mat: akka.stream.Materializer
[error]       status(home) mustBe OK
[error]             ^

HomeController:

def index() = Action { implicit request =>
  Ok
}

def json() = Action(parse.json) { implicit request =>
  Ok
}

HomeControllerSpec:

class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
  "HomeController POST" should {
    "answer Ok" in {
      val controller = new HomeController(stubControllerComponents())
      val home = controller.json().apply(FakeRequest(POST, "/"))
      status(home) mustBe OK
    }
  }
Saskia
  • 1,046
  • 1
  • 7
  • 23

2 Answers2

3

It seems that you need to inject a materializer in your controller:

class Controller @Inject() (implicit val mat: Materializer) { ??? }

Learner
  • 1,170
  • 11
  • 21
  • It works fine when I run the Application but not in tests. I found https://stackoverflow.com/questions/36888253/play-2-5-what-is-akka-stream-materializer-useful-for and added implicits for actorSystem and materializer in my test class. It's compiling but always returns 400 without my method ever called. – Saskia Mar 22 '18 at 09:33
3

There seem to be two issues with the code in question regarding stubControllerComponents and FakeRequest calls.

Helpers.stubControllerComponents by default constructs ControllerComponents with NoMaterializer which simply throws an exception when used, so we need to provide an actual materializer as follows:

       implicit val materializer = ActorMaterializer()(ActorSystem())

       Helpers.stubControllerComponents(
         playBodyParsers = Helpers.stubPlayBodyParsers(materializer)
       ) 

The second issue is with FakeRequest where we need to provide a body as follows (otherwise we get 4xx error) :

FakeRequest(POST, "/json").withBody(Json.obj("name" -> "Jon Doe"))

Taking into account the above we can write the complete test as follows:

class HomeControllerSpec extends PlaySpec with GuiceOneAppPerTest {
  "HomeController POST" should {
    "answer Ok" in {
       implicit val materializer = ActorMaterializer()(ActorSystem())

       val controllerComponents = 
         Helpers.stubControllerComponents(
           playBodyParsers = Helpers.stubPlayBodyParsers(materializer)
         ) 

       val controller = new HomeController(controllerComponents)

       val fakeRequest = 
         FakeRequest(POST, "/json").withBody(Json.obj("name" -> "Jon Doe"))

       val home = call(controller.json(), fakeRequest)

       status(home) mustBe OK
    }
  }
}

UPDATE 2023

The apply method ActorMaterializer() is deprecated and today the recommended way to build a materializer instance is the following.

implicit val materialzer = SystemMaterializer(ActorSystem()).materializer
spi-x-i
  • 287
  • 1
  • 12
Mario Galic
  • 47,285
  • 6
  • 56
  • 98