This is my source code for testing:
import org.openqa.selenium.firefox.FirefoxDriver
import org.scalatest.FlatSpec
import org.scalatest.Matchers
import play.api.test.FakeApplication
import play.api.test.WithApplication
class FunctionalSpec extends FlatSpec with Matchers {
def withDriver(f: FirefoxDriver => Unit) = {
val driver = new FirefoxDriver
try {
new WithApplication(
new FakeApplication(additionalConfiguration = Map("application.secret" -> "secret"))) {
f(driver)
}
} finally {
driver.quit
}
}
}
It doesn't show any compile-error in Eclipse. But when I execute the test command it shows a very long error message. This is the first line of the errors:
symbol value f$1 does not exist in test.FunctionalSpec$$anon$1$delayedInit$body.apply
I tried to play around a bit by modifying the code as shown below:
def withDriver(f: FirefoxDriver => Unit) = {
val driver = new FirefoxDriver
try {
val g = f
val driver2 = driver
new WithApplication(
new FakeApplication(additionalConfiguration = Map("application.secret" -> "secret"))) {
val h = g
val driver3 = driver2
h(driver3)
}
} finally {
driver.quit
}
}
There are no errors thrown when I executed the test command. Any idea what's happening? Is there any limit of how deep a block of code can go in order to keep the identifiers recognised?
EDIT:
Using my modified code above results in runtime-error. I added the code below:
"The admin" should "be able to login and logout correctly" in withDriver { implicit driver =>
// Don't do anything yet.
}
This one is displayed in one line of the runtime-error messages:
Cause: java.lang.NoSuchFieldError: g$1
Any suggestion for a workaround?
ADDITIONAL NOTE:
All the codes above compile just fine. Neither Eclipse or scalac complain. All the errors above happen only when I do the test command from the Play command shell.
This doesn't directly answer your question, but I would do two things:
Use Specs2 instead of ScalaTest
Rewrite your tests as described here: http://www.playframework.com/documentation/2.2.x/ScalaFunctionalTest
Related
At work we've created this plugin, and from one of its inputKey it's suppose to log an exception (whenever that occurs). For demonstration purposes I've only used the relevant stuff, so have a look at the following code:
theTask := {
streams.value.log
val args: Seq[String] = spaceDelimited("<arg>").parsed
args.foreach { arg =>
val env = SomeLogicThatPotentiallyThrowsException(arg)
}
}
object SomeLogicThatPotentiallyThrowsException {
def apply(arg: String): String = {
if(arg==throwexception) throw Exception("Boom!")
else arg
}
}
so whenever I'm trying to use the plugin and execute the task as follows:
sbt theTask throwexception
The error is only logged whenever I've added streams.value.log as a statement in the theTask task..
So yeah.. am I doing anything wrong here or is this a bug in sbt?
Thanks for reading people
Using Play 2.7. In a test class, I want to test a service that reads the configuration (properties) file conf/fun.conf.
I could not find any example how to carry out this feat. I have looked at the Play documentation and also questions here, but found nothing.
The service I wrote gets Configuration through injection:
class MyFunService #Inject() (config: Configuration) extends FunService {
override def getValue(key: String) = config.get[String](key)
}
I want to call this service from a test:
class FunSpec(implicit ee: ExecutionEnv) extends Specification {
sequential
"The FunService" should {
"retrieve a value" in new WithApplication() {
//Oops! Does not actually read the conf file...
val env = play.api.Environment.simple()
val config = play.api.Configuration.load(env)
//...so getting an Exception here.
val f = Future(new MyFunService(config).getValue("fun_stuff"))
f must beEqualTo("having_fun").await(retries = 0, timeout = 5.seconds)
}
}
}
I'm using this code to read test resources
import com.google.common.base.Charsets.UTF_8
import com.google.common.io.Resources
object ResourceFileSupport {
def read(file: String) =
Resources.toString(
Option( this.getClass.getClassLoader.getResource(file) ).getOrElse {
throw new IllegalArgumentException(s"resource $file not found in classpath.")
}, UTF_8)
}
Consider the following code:
import chisel3._
import chisel3.util._
import chisel3.iotesters._
class Inverter extends Module {
val io = IO(new Bundle {
val a = Input(UInt(4.W))
val s = Output(UInt(4.W))
})
io.s := ~io.a
}
class InverterTester(c: Inverter) extends PeekPokeTester(c) {
poke(c.io.a, 8)
step(1)
expect(c.io.s, 8) // Should be 7 here
}
object TestMain extends App {
chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
}
Now I run sbt 'test:runMain TestMain' and got this line (info is in purple):
[info] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
And the exit value of sbt is zero.
I need that line to be an error (with red color):
[error] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
As well as making the above sbt command exit with a non-zero value.
How can I achieve it with minimal change to existing code?
First the easy part. You can get a non-zero result code by using the result of chisel.execute like this.
val result = chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
System.exit(if(result) 0 else 1)
Changing the logging level, unfortunately requires changing each of the separate backends in the chisel-testers repo. The following is an example of changing the TreadleBackend.scala, one of the three.
def expect(signal: InstanceId, expected: BigInt, msg: => String)
(implicit logger: TestErrorLog, verbose: Boolean, base: Int) : Boolean = {
signal match {
case port: Element =>
val name = portNames(port)
val got = treadleTester.peek(name)
val good = got == expected
if (!good) {
logger error
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
else if (verbose) {
logger info
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
if(good) treadleTester.expectationsMet += 1
good
case _ => false
}
}
This would not be an unreasonable issue to file, I think a logger.error would make more sense on an expect failure. There is some concern that changing this could have unexpected consequences for existing users, who are looking for that string.
But I'd like to encourage you to take a look at freechipsproject/chisel-testers2 repo. It's where the team is putting most of their testing development time. It would be easier to change, it has a lot of other nice features for building unit tests, and we are looking at ways we can make it better than chisel-testers.
EDIT2:
So another heads up on this:
I still have no idea why this happens, but I have now a similar problem with jOOQ and the Dialect I have to it. My code here looks like this:
object MyDB {
private lazy val dialect = SQLDialect.POSTGRES
def withSession[T](f: DSLContext => T) = f(DSL.using(getConnectionPool, dialect))
}
if I remove the "lazy" it blows up when I try to execute jOOQ queries in line 552 of https://github.com/jOOQ/jOOQ/blob/version-3.2.0/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java
That happens to be a line where the dialect is evaluated. After I added the lazy everything works as expected.
Maybe this is an issue with the threading of LiftWeb and the executing thread does not see the correct value of the val? I have no idea...
EDIT:
I have found a way to do what I want simply by adding a lazy to the values in the first, broken version. So with lazy vals it all works well.
However I'll let this stay open, as I have no idea how to explain this behavior.
Original Post:
So I am trying to use Parameterized Queries in Slick.
My code is below, my problem is that I get an NPE (see comments) when I try to run this from within the webapplication (liftweb, container started with sbt) (the application creates an object of the class PlayerListCollector that is given the string "cola")
When I execute the object as App from within Eclipse the println at the bottom works just fine.
class PlayerListCollector(term: String) {
import PlayerListCollector._
val searchResult = executeSearch(term)
}
object PlayerListCollector extends Loggable with App{
private val searchNameCurrent = Parameters[String].flatMap {
case (term) => {
for {
p <- Players if p.uberName.isNotNull
n <- p.displayName if (n.displayName.toLowerCase.like(term))
} yield (p.id, n.displayName)
}
}
private def executeSearch(term: String) = {
val lowerTerm = "%"+term.toLowerCase()+"%"
logger info "HELLO " +lowerTerm // prints HELLO %cola%
val foo = searchNameCurrent(lowerTerm) // NPE right in this line
logger info foo // never executed from here on ...
val byCurrent = foo.list
logger info byCurrent
[...]
}
// this works if run directly from within eclipse!
println(DB withSession {
searchNameCurrent("%cola%").list
})
}
The problem vanishes when I change the code to look like this:
[...]
object PlayerListCollector extends Loggable with App{
private def executeSearch(term: String) = {
val searchNameCurrent = Parameters[String].flatMap {
case (term) => {
for {
p <- Players if p.uberName.isNotNull
n <- p.displayName if (n.displayName.toLowerCase.like(term))
} yield (p.id, n.displayName)
}
}
val lowerTerm = "%"+term.toLowerCase()+"%"
logger info "HELLO " +lowerTerm // prints HELLO %cola%
val foo = searchNameCurrent(lowerTerm) // executes just fine when the query is in a local val
logger info foo
val byCurrent = foo.list
logger info byCurrent // prints expected output
[...]
}
[...]
}
I have no idea whatsoever why this happens.
Isn't the whole point of a paramterized query to put it in a val that is only once filled with a value so it does not need to be compiled multiple times?
So it turns out I used the App-Trait (http://www.scala-lang.org/api/current/index.html#scala.App) on these objects.
Reading the big fat caveat tells us what is happening I guess.
I want to run unit tests for a Play 2 Scala app using the same database setup as used in production: Slick with Postgres. The following fails with "java.sql.SQLException: Attempting to obtain a connection from a pool that has already been shutdown." on the 2nd test.
package controllers
import org.specs2.mutable._
import play.api.db.DB
import play.api.Play.current
import play.api.test._
import play.api.test.Helpers._
import scala.slick.driver.PostgresDriver.simple._
class BogusTest extends Specification {
def postgresDatabase(name: String = "default",
options: Map[String, String] = Map.empty): Map[String, String] =
Map(
"db.test.driver" -> "org.postgresql.Driver",
"db.test.user" -> "postgres",
"db.test.password" -> "blah",
"db.test.url" -> "jdbc:postgresql://localhost/blah"
)
def fakeApp[T](block: => T): T =
running(FakeApplication(additionalConfiguration =
postgresDatabase("test") ++ Map("evolutionplugin" -> "disabled"))) {
def database = Database.forDataSource(DB.getDataSource("test"))
database.withSession { implicit s: Session => block }
}
"Fire 1" should {
"do something" in fakeApp {
success
}
}
"Fire 2" should {
"do something else" in fakeApp {
success
}
}
}
I run the test like this:
$ play -Dconfig.file=`pwd`/conf/dev.conf "test-only controllers.BogusTest"
Two other mysteries:
1) All tests run, even though I ask for just BogusTest to run
2) application.conf is always used, not def.conf, and the driver information comes from application.conf, not the info configured in the code.
This is a tentative answer as I have currently tested on play 2.2.0 and I can't reproduce your bug, using a MYSQL database.
I feel there might be a very tricky bug in your code. First of all, if you explore the DBPlugin implementation provided by Play, BoneCPPPlugin:
/**
* Closes all data sources.
*/
override def onStop() {
dbApi.datasources.foreach {
case (ds, _) => try {
dbApi.shutdownPool(ds)
} catch { case NonFatal(_) => }
}
val drivers = DriverManager.getDrivers()
while (drivers.hasMoreElements) {
val driver = drivers.nextElement
DriverManager.deregisterDriver(driver)
}
}
You see that the onStop() method closes the connection pool. So it's clear, you are providing to the second test example an application which has already been stopped (and therefore its plugins are stopped and the db connectin pool closed).
Scalatests and specs2 run the test in parallel, and you can rely on the test helper because it's thread-safe:
def running[T](fakeApp: FakeApplication)(block: => T): T = {
synchronized {
try {
Play.start(fakeApp)
block
} finally {
Play.stop()
play.api.libs.ws.WS.resetClient()
}
}
}
However, when you do
DB.getDataSource("test")
From the source code of Play:
def getDataSource(name: String = "default")(implicit app: Application): DataSource = app.plugin[DBPlugin].map(_.api.getDataSource(name)).getOrElse(error)
So here there is an implicit, does which not get resolved to FakeApplication (it is not an implicit in scope!!!), but to Play.current and it appears that in the second case, this is not what you were expecting it to be, Play.current still point to the previous instance of FakeApplication: it probably depends on how implicit are captured in closures
If you however, refactor the fakeApp method, you can ensure the application you just created is used to resolve the implicit (you can always make explicit the value for an implicit parameter)
def fakeApp[T](block: => T): T = {
val fakeApplication = FakeApplication(additionalConfiguration =
postgresDatabase("test") ++ Map("evolutionplugin" -> "disabled"))
running(fakeApplication) {
def database = Database.forDataSource(DB.getDataSource("test")(fakeApplication))
database.withSession { implicit s: Session => block }
}
}