Scala noob here.
I don't understand the following piece of code, taken from a Play application's integration test (Scala):
package workflows.admin
import play.api.test._
class SignInSpec extends PlaySpecification {
"An activated user" should {
"be able to sign in to the admin console" in new WithBrowser(webDriver = WebDriverFactory(FIREFOX)) {
// some matchers here ...
true
}
}
}
As far as I understand, the example creates a new anonymous class from the abstract class WithBrowser
and instantiates it. The instance will receive the (named) constructor parameter webDriver
.
The problem is that I don't understand what's going on here when looking into WithBrowser
:
abstract class WithBrowser[WEBDRIVER <: WebDriver](
val webDriver: WebDriver = WebDriverFactory(Helpers.HTMLUNIT),
val app: Application = GuiceApplicationBuilder().build(),
val port: Int = Helpers.testServerPort) extends Around with Scope {
def this(
webDriver: Class[WEBDRIVER],
app: Application,
port: Int) = this(WebDriverFactory(webDriver), app, port)
implicit def implicitApp: Application = app
implicit def implicitPort: Port = port
lazy val browser: TestBrowser = TestBrowser(webDriver, Some("http://localhost:" + port))
override def around[T: AsResult](t: => T): Result = {
try {
Helpers.running(TestServer(port, app))(AsResult.effectively(t))
} finally {
browser.quit()
}
}
}
I have two questions:
WithBrowser
is a generic abstract class with a type parameter WEBDRIVER
, but the type parameter is not declared in the example. Instead, the anonymous class' instance receives this information using the named constructor parameter webDriver
. What's the missing link between the type parameter and the constructor parameter ?true
) and this block of code is the test itself. But where does that code go in the anonymous class ? WithBrowser
extends Around
and overrides the around
function, which executes the code block, but I don't understand how the generated anonymous class moves the given example code block into around
.Any help is highly appreciated.
Scala can infer type parameters. Since none of the passed parameters' type is somehow related to the type parameter and would thereby restrict the type to be inferred, the scala compiler will just infer it as type Nothing
.
The code block would normally be the constructor of the class, but this is a special case. The Around
class does extend the scala.DelayedInit
interface. This will rewrite the code within the constructor to a call of the delayedInit
function. The code that would actually be the constructor is passed as call-by-name parameter to this function. This value (actually wrapped in a org.specs2.execute.Result.resultOrSuccess
call) is the parameter passed to the around
function.
Imagine the Around
class like this:
class Around extends DelayedInit {
def delayedInit(f: => Unit): Unit = around(f)
def around(f: => Unit): Unit
}
Let's say you will now inherit the around class:
class Foo extends Around {
println("Hello World")
}
The above gets rewritten to this:
class Foo extends Around {
delayedInit(println("Hello World"))
}
If my explanation was not clear enough or you want to know more implementation details:
- Here is Around
and the DelayedInit
documentation.