问题
I thought I had fixed the problem temporarily, but it turns out I am still having problems.
I am trying to create some specs2 tests for my models layer, I would like to insert some dummy object, and then run some queries to make sure data comes out as expected. Here is what my simple test looks like:
class ModelSpec extends Specification { override def is = args(sequential = true) ^ super.is object FakeApp extends FakeApplication() running(FakeApp){ println("set up database") val newUser = User( email = "wfbarksdale@gmail.com", username = "weezybizzle", password = "nutterbutter") User.save(newUser) } running(FakeApp) { "User Model" should { "be created and retrieved by username" in { println("finding someone") User.findOneByUsername("weezybizzle") must beSome } "not find non existant user" in { println("finding nobody") User.findOneByUsername("nobody") must beNone } } } }
And here is the stack trace I get from the unit tests:
[info] Compiling 1 Scala source to /www/mojulo3/target/scala-2.9.1/test-classes... set up database finding someone finding nobody [info] ModelSpec [info] [info] User Model should [error] ! be created and retrieved by username [error] IllegalStateException: this Mongo has been closed (DBTCPConnector.java:123) [error] com.mongodb.DBTCPConnector._checkClosed(DBTCPConnector.java:123) [error] com.mongodb.DBTCPConnector.call(DBTCPConnector.java:207) [error] com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:313) [error] com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:298) [error] com.mongodb.DBCollection.findOne(DBCollection.java:682) [error] com.mongodb.DBCollection.findOne(DBCollection.java:661) [error] com.mongodb.casbah.MongoCollectionBase$class.findOne(MongoCollection.scala:225) [error] com.mongodb.casbah.MongoCollection.findOne(MongoCollection.scala:897) [error] com.novus.salat.dao.SalatDAO.findOne(SalatDAO.scala:311) [error] models.User$.findOneByUsername(User.scala:24) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$2$$anonfun$apply$3.apply(ModelSpec.scala:29) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$2$$anonfun$apply$3.apply(ModelSpec.scala:29) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$2.apply(ModelSpec.scala:29) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$2.apply(ModelSpec.scala:27) [error] ! not find non existant user [error] IllegalStateException: this Mongo has been closed (DBTCPConnector.java:123) [error] com.mongodb.DBTCPConnector._checkClosed(DBTCPConnector.java:123) [error] com.mongodb.DBTCPConnector.call(DBTCPConnector.java:207) [error] com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:313) [error] com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:298) [error] com.mongodb.DBCollection.findOne(DBCollection.java:682) [error] com.mongodb.DBCollection.findOne(DBCollection.java:661) [error] com.mongodb.casbah.MongoCollectionBase$class.findOne(MongoCollection.scala:225) [error] com.mongodb.casbah.MongoCollection.findOne(MongoCollection.scala:897) [error] com.novus.salat.dao.SalatDAO.findOne(SalatDAO.scala:311) [error] models.User$.findOneByUsername(User.scala:24) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$6$$anonfun$apply$7.apply(ModelSpec.scala:35) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$6$$anonfun$apply$7.apply(ModelSpec.scala:35) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$6.apply(ModelSpec.scala:35) [error] test.ModelSpec$$anonfun$3$$anonfun$apply$1$$anonfun$apply$6.apply(ModelSpec.scala:33) [info] [info] [info] Total for specification ModelSpec [info] Finished in 20 ms [info] 2 examples, 0 failure, 2 errors [info] [error] Error: Total 2, Failed 0, Errors 2, Passed 0, Skipped 0 [error] Error during tests: [error] test.ModelSpec [error] {file:/www/mojulo3/}mojulo3/test:test: Tests unsuccessful [error] Total time: 4 s, completed Aug 28, 2012 10:02:33 PM
It looks like the FakeApp is somehow disconnecting from the database, and not reconnecting. I looked into the Play source, and it seems that the application will be stopped and restarted, regardless of whether or not it is actually the same object. I am thinking that the issue may be stemming from the salat onStop()
method or onStart()
but I am not really sure.
I have been struggling for a while with this, any help, even just on how to reason about the problem, would be greatly appreciated.
回答1:
This is because it closes the MongoDB connection in the plugin's onStop method. I've submitted a pull request so that this doesn't happen during testing:
https://github.com/leon/play-salat/pull/27
回答2:
I've changed the implementation of how play-salat closes it's connections.
Usually it's a good thing closing down all the connections when the app stops, because the plugin will be re instantiated when the app starts again.
The problem was I was creating the mongodb connection in a lazy val which created the connection once, and when the app stopped it simply calls .close() on it.
What I've done to fix it is that the connection is now closed and if you ask for the connection again it creates a new one and passes it to you.
This is all available in the 1.1-SNAPSHOT version of play-salat which you can use straight away by adding
resolvers += "OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
This release also includes salat 1.9.1 and support for capped collections and gridfs :)
Try it out and give me a shout if anything doesn't work as expected.
If everything looks good, I'll release the final 1.1 soon.
回答3:
There are 2 phases in executing a specs2 Specification: first the specification is created, with all its examples. Then it is executed.
What you should do instead is:
class ModelSpec extends Specification with AroundExamples {
// note the use of sequential here which is simpler than
// overriding the "is" method
sequential
object FakeApp extends FakeApplication()
// this method, defined in the AroundExamples trait
// makes sure that every example is executed "inside"
// the fake app.
def around[R <% Result](r: =>R) = running(FakeApp)(r)
// this will be only executed after the whole spec is created
// if anything fails here, an exception will be caught, reported
// and the rest will not execute
step {
running(FakeApp) {
println("set up database")
val newUser = User(
email = "wfbarksdale@gmail.com",
username = "weezybizzle",
password = "nutterbutter")
User.save(newUser)
}
}
"User Model" should {
"be created and retrieved by username" in {
println("finding someone")
User.findOneByUsername("weezybizzle") must beSome
}
"not find non existant user" in {
println("finding nobody")
User.findOneByUsername("nobody") must beNone
}
}
}
}
I hope that helps.
来源:https://stackoverflow.com/questions/12170009/still-cant-run-multiple-tests-against-play-fakeapp-with-salat-casbah