I'm quite new to Play2 and Scala. I'm writing a simple application that uses ReactiveMongo plugin.
I wrote a simple object to use as DAO
object UserDAO {
def db: reactivemongo.api.DB = ReactiveMongoPlugin.db
def collection: JSONCollection = db.collection[JSONCollection]("users")
collection.indexesManager.ensure(Index(List("name" -> IndexType.Ascending),
unique = true))
def insert(User): Future[LastError] = {
collection.insert(unit)
}
def findOne(query: JsObject): Future[Option[User]] = {
collection.find(query).one[User]
}
def removeOne(query: JsObject): Future[LastError] = {
collection.remove(query, firstMatchOnly = true)
}
...
}
Note that I create an index to ensure that two Users with the same name cannot be created. In this way I can use the DAO in my controllers as follow
class Users extends Controller with MongoController {
def createUser = Action.async(parse.json) {
request =>
request.body.validate[User].map {
user =>
UserDAO.insert(user).map {
lastError =>
Created(s"User Created")
}
}.getOrElse(Future.successful(BadRequest("invalid json")))
}
}
So far so good. The problems come with Specs Tests. I have two test suites, and I configured them to work on different databases. The first test suite uses the database "mydb1":
val addConf = Map("mongodb.uri" -> ("mongodb://localhost:27017/mydb1"))
class UsersTest1 extends PlaySpecification {
sequential
"Users" should {
"create a User" in new WithApplication(FakeApplication(
additionalConfiguration = addConf)) {
val request = FakeRequest(POST, "/user")
.withJsonBody(Json.obj(
"name" -> "foo",
"age" -> 3))
val response = route(request)
...
...
}
}
}
The second test suite uses the database "mydb2"
val addConf = Map("mongodb.uri" -> ("mongodb://localhost:27017/mydb2"))
class UsersTest2 extends PlaySpecification {
sequential
"Users" should {
"create a User" in new WithApplication(FakeApplication(
additionalConfiguration = addConf)) {
val request = FakeRequest(POST, "/user")
.withJsonBody(Json.obj(
"name" -> "foo",
"age" -> 3))
val response = route(request)
...
...
}
}
}
The problem is that after a complete test run, using the mongo CLI I see that only in one of the two resulting databases the index is actually present.
It looks like the UserDAO singleton object instance is shared between all FakeApplications, so that the call to collection.indexesManager.ensure(...) is executed only once for all the tests, when the object is accessed for the first time.
As a proof of that I tried to move the collection.indexesManager.ensure(...) call inside the UserDAO.insert() function, and actually it solves the problem.
I used to think to FakeApplications as totally isolated instances of the application.
Unforutnately, yes they do. This makes writing parallel tests very hard (or even impossible). This will change in Play 2.4: https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
来源:https://stackoverflow.com/questions/29850198/play2-framework-scala-specs2-do-different-fakeapplications-share-singleton-obj