Play2 Framework/Scala/Specs2. Используют ли разные FakeApplications общие объекты Singleton?

Я новичок в Play2 и Scala. Я пишу простое приложение, использующее плагин ReactiveMongo.

Я написал простой объект для использования в качестве 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)
  }

  ...
}

Обратите внимание, что я создаю индекс, чтобы гарантировать невозможность создания двух пользователей с одинаковым именем. Таким образом, я могу использовать DAO в своих контроллерах следующим образом.

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")))
  }
}

Все идет нормально. Проблемы приходят с тестами спецификаций. У меня есть два набора тестов, и я настроил их для работы с разными базами данных. Первый набор тестов использует базу данных «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)
      ...
      ...
    }
  }
}

Второй набор тестов использует базу данных «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)
      ...
      ...
    }
  }
}

Проблема в том, что после полного тестового прогона с помощью CLI mongo я вижу, что только в одной из двух полученных баз данных индекс действительно присутствует.

Похоже, экземпляр одноэлементного объекта UserDAO является общим для всех FakeApplications, поэтому вызов collection.indexesManager.ensure(...) выполняется только один раз для всех тестов, когда к объекту обращаются в первый раз.

В качестве доказательства я попытался переместить вызов collection.indexesManager.ensure(...) внутрь функции UserDAO.insert(), и на самом деле это решает проблему.

Раньше я думал о FakeApplications как о полностью изолированных экземплярах приложения.


person tano    schedule 24.04.2015    source источник


Ответы (1)


К сожалению, да. Это делает написание параллельных тестов очень трудным (или даже невозможным). Это изменится в Play 2.4: https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection

person pathikrit    schedule 24.04.2015
comment
Или вы можете самостоятельно управлять соединениями ReactiveMongo. Поскольку RM предоставляет пулы, это не повлияет на производительность. - person cchantep; 25.04.2015