Search code examples
scalaplayframeworkscalatestplayframework-2.6

How do I test Play REST API with Json BodyParser?


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
    }
  }

Solution

  • 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