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 :)
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.