Play 2.4 测试出错:CacheManager 已关闭。不能再使用了
Error with Play 2.4 Tests : The CacheManager has been shut down. It can no longer be used
我们的应用程序基于 Play 2.4 与 Scala 2.11 和 Akka。
使用的数据库是 MySQL.
在我们的 application.We 中大量使用缓存,使用 Play 的默认 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)
}
}
}
我们使用 Play 的内置 Specs2 进行测试。
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(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)
[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(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)
[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.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.apply(Application.scala:234)
[error] play.api.Application$$anonfun$instanceCache.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
我们期待帮助解决上述问题或实现专门用于测试的模拟缓存的方法。
提前致谢。
我遇到了类似的问题,所以我删除了一个缓存实现并将其换入以进行测试。
class FakeCache extends CacheApi {
override def set(key: String, value: Any, expiration: Duration): Unit = {}
override def get[T](key: String)(implicit evidence: ClassManifest[T]): Option[T] = None
override def getOrElse[A](key: String, expiration: Duration)(orElse: => A)(implicit evidence: 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 接口,详见此处:
我们的应用程序基于 Play 2.4 与 Scala 2.11 和 Akka。 使用的数据库是 MySQL.
在我们的 application.We 中大量使用缓存,使用 Play 的默认 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)
}
}
}
我们使用 Play 的内置 Specs2 进行测试。
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(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)
[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(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)
[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.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.apply(Application.scala:234)
[error] play.api.Application$$anonfun$instanceCache.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
我们期待帮助解决上述问题或实现专门用于测试的模拟缓存的方法。
提前致谢。
我遇到了类似的问题,所以我删除了一个缓存实现并将其换入以进行测试。
class FakeCache extends CacheApi {
override def set(key: String, value: Any, expiration: Duration): Unit = {}
override def get[T](key: String)(implicit evidence: ClassManifest[T]): Option[T] = None
override def getOrElse[A](key: String, expiration: Duration)(orElse: => A)(implicit evidence: 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 接口,详见此处: