scalaplayframeworkinternationalizationplayframework-2.6twirl

Play Framework with Internationalization test fails


When I run my Play Scala 2.6 app with Internationalization enabled, the page is rendered with all the content being picked up properly from my messages.en file.

But when I test my controller, one of the tests is failing when it checks the content of the page (contentAsString(result) must include ("Welcome to Play")).

The test fails because the content of the page is rendered as label.welcome_to_play instead of Welcome to Play!. This happens only when I render the index page from a new instance of the controller. When I test by rendering the page from the app or the router, the same test (contentAsString) is passing.

Do you have any idea why this is happening?

This is my HomeController.scala:

@Singleton
class HomeController @Inject()(cc: ControllerComponents)
  extends AbstractController(cc) with I18nSupport {

  def index() = Action { implicit request: Request[AnyContent] =>
    Ok(views.html.index())
  }
}

And this is my HomeControllerSpec.scala test:

class HomeControllerSpec extends PlaySpec
  with GuiceOneAppPerTest
  with Injecting
  with Results {

  "Calling HomeController.index" should {

    "render the index page from a new instance of controller" in {
      val controller = new HomeController(stubControllerComponents())
      val result = controller.index().apply(FakeRequest(GET, "/"))

      status(result) mustBe OK
      contentType(result) mustBe Some("text/html")
      // THIS IS WHERE THE TEST FAILS. Content appears as "label.welcome_to_play"
      contentAsString(result) must include ("Welcome to Play")
    }

    "render the index page from the application" in {
      val controller = inject[HomeController]
      val home = controller.index().apply(FakeRequest(GET, "/"))

      status(home) mustBe OK
      contentType(home) mustBe Some("text/html")
      // WHILE THIS IS PASSING
      contentAsString(home) must include ("Welcome to Play")
    }

    "render the index page from the router" in {
      val request = FakeRequest(GET, "/")
      val home = route(app, request).get

      status(home) mustBe OK
      contentType(home) mustBe Some("text/html")
      // AND THIS IS PASSING
      contentAsString(home) must include ("Welcome to Play")
    }
  }
}

And this is my index.scala.html view:

@()(implicit messagesProvider: MessagesProvider)

@main(messagesProvider.messages("label.welcome_to_play")) {
  <h1>@messagesProvider.messages("label.welcome_to_play")</h1>
}

This is how my messages.en file looks like:

label.welcome_to_play = Welcome to Play!

I have the following configuration in my conf file:

play.i18n {
  langs = [ "en" , "lv ]
}

I tried renaming the file from messages.en to messages but it didn't help.

Does anyone know why the test fails? Why doesn't the messages file being used when rendering the page from a new instance of my controller? Thank you in advance for your help :)


Solution

  • I eventually ended up instantiating DefaultMessagesApi with a testMessages map as follows:

    First, I created a testMessages map inside my HomeControllerSpec:

    private val testMessages: Map[String, Map[String, String]] = Map(
      "en" -> Map(
        "label.welcome_to_play" -> "Welcome to Play!",
        "label.hello_world" -> "Hello world"
      )
    )
    

    This map contains only a short list of the labels that I want to test and is not a copy of the full messages.en file.

    Then in the failing test, I create a new instance of the HomeController by overriding the messagesApi...

    "render the index page from a new instance of controller" in {
      val controller = new HomeController(stubControllerComponents()) {
        override val messagesApi = new DefaultMessagesApi(testMessages)
      }
      val result: Future[Result] = controller.index().apply(FakeRequest(GET, "/"))
    
      status(result) mustBe OK
      contentType(result) mustBe Some("text/html")
      contentAsString(result) must include ("Welcome to Play")
    }
    

    ... and the test is passing.

    I acknowledge that this approach causes some duplication (message labels both inside the messages.en file and the Spec) but for small tests with not many messages to test, I think it's an acceptable solution.