Play2 Framework/Scala/Specs2 - Do different FakeApplications share Singleton objects?

北战南征 提交于 2019-12-10 09:49:14

问题


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.


回答1:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!