Ошибка при тестах Play 2.4: CacheManager был закрыт. Это больше не может быть использовано

Наше приложение построено на Play 2.4 с Scala 2.11 и Akka. База данных используется MySQL.

Кэш интенсивно используется в нашем приложении. Мы используем EhCache по умолчанию для кэширования.

Наш пример кода:

import play.api.Play.current
import play.api.cache.Cache

case class Sample(var id: Option[String],
                 //.. other fields
)

class SampleTable(tag: Tag)
  extends Table[Sample](tag, "SAMPLE") {
  def id = column[Option[String]]("id", O.PrimaryKey)
  // .. other field defs
}

object SampleDAO extends TableQuery(new SampleTable(_)) with SQLWrapper {
  def get(id: String) : Future[Sample] = {
    val cacheKey = // our code to generate a unique cache key
    Cache.getOrElse[Future[[Sample]](cacheKey) {
      db.run(this.filter(_.id === id).result.headOption)
    }
  }
}

Мы используем встроенные спецификации Plays2 для тестирования.

var id = "6879a389-aa3c-4074-9929-cca324c7a01f"

  "Sample Application " should {
    "Get a Sample" in new WithApplication {
      val req = FakeRequest(GET, s"/v1/samples/$id")
      val result = route(req).get
      assertEquals(OK, status(result))
      id = (contentAsJson(result).\("id")).get.toString().replaceAllLiterally("\"", "")
   }

Но при юнит-тестировании мы часто сталкиваемся с ошибкой ниже.

[error]    1) Error in custom provider, java.lang.IllegalStateException: The  CacheManager has been shut down. It can no longer b
e used.
[error]      at play.api.cache.EhCacheModule.play$api$cache$EhCacheModule$$bindCache$1(Cache.scala:181):
[error]    Binding(interface net.sf.ehcache.Ehcache qualified with QualifierInstance(@play.cache.NamedCache(value=play)) to Prov
iderTarget(play.api.cache.NamedEhCacheProvider@7c8b0968)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.ap
i.inject.guice.GuiceableModuleConversions$$anon$1)
[error]      while locating net.sf.ehcache.Ehcache annotated with @play.cache.NamedCache(value=play)
[error]      at play.api.cache.EhCacheModule.play$api$cache$EhCacheModule$$bindCache$1(Cache.scala:182):
[error]    Binding(interface play.api.cache.CacheApi qualified with QualifierInstance(@play.cache.NamedCache(value=play)) to Pro
viderTarget(play.api.cache.NamedCacheApiProvider@38514c74)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.
api.inject.guice.GuiceableModuleConversions$$anon$1)
[error]      while locating play.api.cache.CacheApi annotated with @play.cache.NamedCache(value=play)
[error]      while locating play.api.cache.CacheApi
[error]
[error]    1 error (InjectorImpl.java:1025)
[error] com.google.inject.internal.InjectorImpl$2.get(InjectorImpl.java:1025)
[error] com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
[error] play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321)
[error] play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316)
[error] play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[error] play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[error] play.utils.InlineCache.fresh(InlineCache.scala:69)
[error] play.utils.InlineCache.apply(InlineCache.scala:62)
[error] play.api.cache.Cache$.cacheApi(Cache.scala:63)
[error] play.api.cache.Cache$.getOrElse(Cache.scala:106

Мы с нетерпением ждем помощи в решении вышеуказанной проблемы или в способах внедрения фиктивного кэша исключительно для тестирования.

Заранее спасибо.

3 ответа

Решение

У меня была похожая проблема, поэтому я заглушил реализацию кеша и подменил ее для тестов.

class FakeCache extends CacheApi {
  override def set(key: String, value: Any, expiration: Duration): Unit = {}

  override def get[T](key: String)(implicit evidence$2: ClassManifest[T]): Option[T] = None

  override def getOrElse[A](key: String, expiration: Duration)(orElse: => A)(implicit evidence$1: ClassManifest[A]): A = orElse

  override def remove(key: String): Unit = {}
}

Отменить ввод:

class AbstractViewTest extends PlaySpecification {

  def testApp(handler: DeadboltHandler): Application = new GuiceApplicationBuilder()
                                                   .overrides(bind[CacheApi].to[FakeCache])
                                                   .in(Mode.Test)
                                                   .build()

}

Вы можете увидеть, как я использую это на GitHub: https://github.com/schaloner/deadbolt-2-scala/blob/master/code/test/be/objectify/deadbolt/scala/views/AbstractViewTest.scala

Другим решением было бы позвонить sequential метод в начале каждого теста.

class MySpec extends Specification {
  sequential

  ...
}

Заметка

parallelExecution in Test := false

Должен быть установлен в build.sbt файл тоже.

Я думаю, что обходить тестирование кеша путем фальсификации кеша - плохая практика. Это делает ваши тесты недействительными, потому что вы пытаетесь не использовать EhCache в масштабируемом и распределенном виде. Гораздо лучший подход - реализовать интерфейс @Singleton, как описано здесь: /questions/28355397/kak-ispolzovat-playcachecacheapi-v-staticheskom-metode-v-play-framework-242/28355413#28355413

Благодаря Mon Calamari

Другие вопросы по тегам