Let's look at some of Play's test cases to see how to use the helper methods. For example, consider the DevErrorPageSpec
test, which is defined as follows:
object DevErrorPageSpec extends PlaySpecification{ "devError.scala.html" should { val testExceptionSource = new play.api.PlayException.ExceptionSource("test", "making sure the link shows up") { ... } …. "show prod error page in prod mode" in { val fakeApplication = new FakeApplication() { override val mode = play.api.Mode.Prod } running(fakeApplication) { val result = DefaultGlobal.onError(FakeRequest(), testExceptionSource) Helpers.contentAsString(result) must contain("Oops, an error occurred") } } } }
This test starts FakeApplication
with the Prod mode and checks the response when FakeRequest
encounters an exception.
FakeApplication
extends an application and is defined as follows:
case class FakeApplication(config: Map[String, Any] = Map(), path: File = new File("."), sources: Option[SourceMapper] = None, mode: Mode.Mode = Mode.Test, global: GlobalSettings = DefaultGlobal, plugins: Seq[Plugin] = Nil) extends Application { val classloader = Thread.currentThread.getContextClassLoader lazy val configuration = Configuration.from(config) }
The method that is running is part of PlayRunners and executes a block of code in the context of a given application. It is defined as follows:
def running[T](app: Application)(block: => T): T = { synchronized { try { Play.start(app) block } finally { Play.stop() } } }
PlayRunners has a few more definitions of how to run, these are:
running[T](testServer: TestServer)(block: => T)
: T
: This can be used to execute a block of code in a running server.running[T](testServer: TestServer, webDriver: WebDriver)(block: TestBrowser => T): T
: This can be used to execute a block of code in a running server with a test browser.running[T, WEBDRIVER <: WebDriver](testServer: TestServer, webDriver: Class[WEBDRIVER])(block: TestBrowser => T): T
: This can also be used to execute a block of code in a running server with a test browser using Selenium WebDriver. This method uses the previous method internally.Instead of using the running
method directly, as an alternative, we could define our tests using the wrapper classes, which make use of the running. There are different helpers for Specs2 and ScalaTest.
First, let's look at the ones available when using Specs2. They are as follows:
WithApplication
: It is used to execute a test within the context of a running application. For example, consider a situation where we want to write functional tests for CountController
, which is responsible for getting a count of distinct data grouped by a perspective. We can write the test as follows:class CountControllerSpec extends PlaySpecification with BeforeExample { override def before: Any = { TestHelper.clearDB } """Counter query""" should { """fetch count of visits grouped by browser names""" in new WithApplication { TestHelper.postSampleData val queryString = """applicationId=39&perspective=browser&from=1389949200000&till=1399145400000""".stripMargin val request = FakeRequest(GET, "/query/count?" + queryString) val response = route(request) val result = response.get status(result) must equalTo(OK) contentAsJson(result) must equalTo(TestHelper.browserCount) } }
Here, assume that TestHelper
is a helper object specifically defined for simplifying the code of test cases (extracting common processes as methods).
If we need to specify FakeApplication
, we can do so by passing it as an argument to the WithApplication
constructor:
val app = FakeApplication() """fetch count of visits grouped by browser names""" in new WithApplication(app) {
This comes in handy when we want to change the default application configurations, GlobalSettings, and so on for the tests.
WithServer
: It is used to execute tests within the context of a running application on a new TestServer
. This is quite useful when we need to start our FakeApplication
on a new TestServer
at a specific port. After slightly modifying the previous example:"""fetch count of visits grouped by browser names""" in new WithServer(app = app, port = testPort) { { ... }
WithBrowser
: It is used to test an application's functionality by performing certain actions in browsers. For example, consider a dummy application where the page title changes on the click of a button. We can test it as follows:class AppSpec extends PlaySpecification { val app: FakeApplication = FakeApplication( withRoutes = TestRoute ) "run in firefox" in new WithBrowser(webDriver = WebDriverFactory(FIREFOX), app = app) { browser.goTo("/testing") browser.$("#title").getTexts().get(0) must equalTo("Test Page") browser.$("b").click() browser.$("#title").getTexts().get(0) must equalTo("testing") }}
We are assuming TestRoute
is a partial function that maps to some of the routes which can then be used in tests.
Now, lets see what
ScalaTestPlus-Play, the library with helper methods that are used for testing with the help of ScalaTest, has to offer. In this section, we will see examples from ScalatestPlus-Play
wherever applicable. The helpers for ScalaTest are as follows:
OneAppPerSuite
: It starts FakeApplication
using Play.start
before running any tests in a suite and then stops it once they are completed. The application is exposed through the variable app and can be overridden if required. From ExampleSpec.scala
:class ExampleSpec extends PlaySpec with OneAppPerSuite { // Override app if you need a FakeApplication with other than non-default parameters. implicit override lazy val app: FakeApplication = FakeApplication(additionalConfiguration = Map("ehcacheplugin" -> "disabled")) "The OneAppPerSuite trait" must { "provide a FakeApplication" in { app.configuration.getString("ehcacheplugin") mustBe Some("disabled") } "make the FakeApplication available implicitly" in { def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key) getConfig("ehcacheplugin") mustBe Some("disabled") } "start the FakeApplication" in { Play.maybeApplication mustBe Some(app) } } }
If we wish to use the same application for all or multiple suites, we can define a nested suite. For such an example, we can refer to NestedExampleSpec.scala
from the library.
OneAppPerTest
: It starts a new FakeApplication
for each test defined in the suite. The application is exposed through the newAppForTest
method and can be overridden if required. For example, consider the OneAppTest
test, where each test uses a different FakeApplication
obtained through newAppForTest
:class DiffAppTest extends UnitSpec with OneAppPerTest { private val colors = Seq("red", "blue", "yellow") private var colorCode = 0 override def newAppForTest(testData: TestData): FakeApplication = { val currentCode = colorCode colorCode+=1 FakeApplication(additionalConfiguration = Map("foo" -> "bar", "ehcacheplugin" -> "disabled", "color" -> colors(currentCode) )) } def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key) "The OneAppPerTest trait" must { "provide a FakeApplication" in { app.configuration.getString("color") mustBe Some("red") } "make another FakeApplication available implicitly" in { getConfig("color") mustBe Some("blue") } "make the third FakeApplication available implicitly" in { getConfig("color") mustBe Some("yellow") } } }
OneServerPerSuite
: It starts a new FakeApplication
and a new TestServer
for the suite. The application is exposed through the variable app and can be overridden if required. The server's port is set from the variable port and can be changed/modified if required. This has been demonstrated in the example for OneServerPerSuite
(ExampleSpec2.scala
):class ExampleSpec extends PlaySpec with OneServerPerSuite { // Override app if you need a FakeApplication with other than non-default parameters. implicit override lazy val app: FakeApplication = FakeApplication(additionalConfiguration = Map("ehcacheplugin" -> "disabled")) "The OneServerPerSuite trait" must { "provide a FakeApplication" in { app.configuration.getString("ehcacheplugin") mustBe Some("disabled") } "make the FakeApplication available implicitly" in { def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key) getConfig("ehcacheplugin") mustBe Some("disabled") } "start the FakeApplication" in { Play.maybeApplication mustBe Some(app) } "provide the port number" in { port mustBe Helpers.testServerPort } "provide an actual running server" in { import java.net._ val url = new URL("http://localhost:" + port + "/boum") val con = url.openConnection().asInstanceOf[HttpURLConnection] try con.getResponseCode mustBe 404 finally con.disconnect() } } }
When we require multiple suites to use the same FakeApplication and TestServer, we can define tests using a nested suite similar to NestedExampleSpec2.scala
.
OneServerPerTest
: It starts a new FakeApplication
and TestServer
for each test defined in the suite. The application is exposed through the newAppForTest
method and can be overridden if required. For example, consider the DiffServerTest
test, where each test uses a different FakeApplication
obtained through newAppForTest
and the TestServer
port is overridden:class DiffServerTest extends PlaySpec with OneServerPerTest { private val colors = Seq("red", "blue", "yellow") private var code = 0 override def newAppForTest(testData: TestData): FakeApplication = { val currentCode = code code += 1 FakeApplication(additionalConfiguration = Map("foo" -> "bar", "ehcacheplugin" -> "disabled", "color" -> colors(currentCode) )) } override lazy val port = 1234 def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key) "The OneServerPerTest trait" must { "provide a FakeApplication" in { app.configuration.getString("color") mustBe Some("red") } "make another FakeApplication available implicitly" in { getConfig("color") mustBe Some("blue") } "start server at specified port" in { port mustBe 1234 } } }
OneBrowserPerSuite
: It provides a new Selenium WebDriver instance per suite. For example, assume that we wish to test the clicking of a button by opening the application in Firefox, the test can be written in the same way as ExampleSpec3.scala
:@FirefoxBrowser class ExampleSpec extends PlaySpec with OneServerPerSuite with OneBrowserPerSuite with FirefoxFactory { // Override app if you need a FakeApplication with other than non-default parameters. implicit override lazy val app: FakeApplication = FakeApplication( additionalConfiguration = Map("ehcacheplugin" -> "disabled"), withRoutes = TestRoute ) "The OneBrowserPerSuite trait" must { "provide a FakeApplication" in { app.configuration.getString("ehcacheplugin") mustBe Some("disabled") } "make the FakeApplication available implicitly" in { def getConfig(key: String)(implicit app: Application) = app.configuration.getString(key) getConfig("ehcacheplugin") mustBe Some("disabled") } "provide a web driver" in { go to ("http://localhost:" + port + "/testing") pageTitle mustBe "Test Page" click on find(name("b")).value eventually { pageTitle mustBe "scalatest" } } } }
We are assuming TestRoute
is a partial function that maps to some of the routes, which can then be used in tests.
The same trait can be used to test the application within multiple browsers, as demonstrated in MultiBrowserExampleSpec.scala
. To execute tests in all the browsers, we should use AllBrowsersPerSuite
, as follows:
class AllBrowsersPerSuiteTest extends PlaySpec with OneServerPerSuite with AllBrowsersPerSuite { // Override newAppForTest if you need a FakeApplication with other than non-default parameters. override lazy val app: FakeApplication = FakeApplication( withRoutes = TestRoute ) // Place tests you want run in different browsers in the `sharedTests` method: def sharedTests(browser: BrowserInfo) = { "navigate to testing "+browser.name in { go to ("http://localhost:" + port + "/testing") pageTitle mustBe "Test Page" click on find(name("b")).value eventually { pageTitle mustBe "testing" } } "navigate to hello in a new window"+browser.name in { go to ("http://localhost:" + port + "/hello") pageTitle mustBe "Hello" click on find(name("b")).value eventually { pageTitle mustBe "helloUser" } } } // Place tests you want run just once outside the `sharedTests` method // in the constructor, the usual place for tests in a `PlaySpec` "The test" must { "start the FakeApplication" in { Play.maybeApplication mustBe Some(app) } }
The trait OneBrowserPerSuite
can also be used with nested tests. Refer to NestedExampleSpec3.scala
.
OneBrowserPerTest
: It starts a new browser session for each test in the suite. This can be noticed by running the ExampleSpec4.scala
test. It's similar to ExampleSpec3.scala
, but OneServerPerSuite
and OneBrowserPerSuite
have been replaced with OneServerPerTest
and OneBrowserPerTest
, respectively, as shown here:@FirefoxBrowser class ExampleSpec extends PlaySpec with OneServerPerTest with OneBrowserPerTest with FirefoxFactory { ... }
We've also replaced the overridden app variable with the newAppForTest
overridden method. Try writing a test that uses the AllBrowsersPerTest
trait.
3.144.114.85