scalatestplus-play
scalatestplus-play copied to clipboard
Use GuiceOneAppPerSuite with async tests
I have a async test suite that extends AsyncFunSpec
.
Now, it seems to be not possible to also mix-in GuiceOneAppPerSuite
:
// does not work
class PostSpec extends AsyncFunSpec with GuiceOneAppPerSuite with Matchers {
it("returns posts") {
val repo = app.injector.instanceOf[PostRepository]
repo.create("my post").map { _ =>
val request = FakeRequest(GET, "/posts")
val result = route(app, request).get
status(result) shouldEqual OK
contentAsJson(result).as[JsArray].value.size shouldEqual 1
}
}
}
I am no scala magician, but I figure this is because GuiceOneAppPerSuite
demands TestSuite
while AsyncFunSpec
inherits from AsyncTestSuite
.
I figure an easy workaround would be to have an (otherwise identical) GuiceOneAppPerSuiteAsync
that uses the AsyncTestSuite
inheritance tree as follows:
/*
* Copyright 2001-2016 Artima, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// this is an adjusted version of
// https://github.com/playframework/scalatestplus-play/blob/master/module/src/main/scala/org/scalatestplus/play/BaseOneAppPerSuite.scala
package org.scalatestplus.play
import org.scalatest.{Args, AsyncTestSuite, AsyncTestSuiteMixin, Status}
import org.scalatestplus.play.FakeApplicationFactory
import play.api.{Application, Play}
/**
* The base abstract trait for one app per suite.
*/
trait BaseOneAppPerSuiteAsync extends AsyncTestSuiteMixin { this: AsyncTestSuite with FakeApplicationFactory =>
/**
* An implicit instance of `Application`.
*/
implicit lazy val app: Application = fakeApplication()
/**
* Invokes `Play.start`, passing in the `Application` provided by `app`, and places
* that same `Application` into the `ConfigMap` under the key `org.scalatestplus.play.app` to make it available
* to nested suites; calls `super.run`; and lastly ensures `Play.stop` is invoked after all tests and nested suites have completed.
*
* @param testName an optional name of one test to run. If `None`, all relevant tests should be run.
* I.e., `None` acts like a wildcard that means run all relevant tests in this `Suite`.
* @param args the `Args` for this run
* @return a `Status` object that indicates when all tests and nested suites started by this method have completed, and whether or not a failure occurred.
*/
abstract override def run(testName: Option[String], args: Args): Status = {
Play.start(app)
try {
val newConfigMap = args.configMap + ("org.scalatestplus.play.app" -> app)
val newArgs = args.copy(configMap = newConfigMap)
val status = super.run(testName, newArgs)
status.whenCompleted { _ => Play.stop(app) }
status
} catch { // In case the suite aborts, ensure the app is stopped
case ex: Throwable =>
Play.stop(app)
throw ex
}
}
}
trait GuiceOneAppPerSuiteAsync extends scala.AnyRef with BaseOneAppPerSuiteAsync with org.scalatestplus.play.guice.GuiceFakeApplicationFactory {
this : GuiceOneAppPerSuiteAsync with org.scalatest.AsyncTestSuite =>
}
Is there an easier way? And if not, should I create a pull request to add these traits?
Looking to do the same. Any suggestions?
Hi @dpoetzsch,
Thanks for investigating this. Another solution is to declare GuiceOneAppPerSuite
with a self-type for Suite
instead of TestSuite
(Suite
is the parent type for both TestSuite
and AsyncTestSuite
):
trait GuiceOneAppPerSuite extends BaseOneAppPerSuite with GuiceFakeApplicationFactory {
this: org.scalatest.Suite =>
}
That way, it should work when you mix GuiceOneAppPerSuite
and AsyncTestSuite
or TestSuite
. If you want to give it a try and submit PR, let me know so I can help you by reviewing it. :-)
Best.
For those looking for a quick hack, you can declare the async suite as a nested suite:
import org.scalatest._
import org.scalatestplus.play.guice.GuiceOneAppPerSuite
import scala.collection.immutable
class GuiceOneAppPerAsyncSuite extends TestSuite with GuiceOneAppPerSuite {
val nestedSuite = new AsyncFlatSpec {
// async tests here
}
override def nestedSuites: immutable.IndexedSeq[Suite] = Vector(nestedSuite)
}
@marcospereira I'm also no Scala magician, but I was trying out your suggestion above to attempt this:
trait GuiceOneAppPerSuite extends BaseOneAppPerSuite with GuiceFakeApplicationFactory {
this: org.scalatest.Suite =>
}
It seems like that won't work because BaseOneAppPerSuite
extends TestSuiteMixin
. Does this mean you're left with rewriting a bunch of the tree with async versions like @dpoetzsch's original suggestion?
took at crack at this, moved TestSuite/TestSuiteMixin to Suite/SuiteMixin: https://github.com/playframework/scalatestplus-play/pull/255