2015-06-25 1 views
10

Наше приложение построено на основе Play 2.4 с Scala 2.11 и Akka. Используемая база данных MySQL.Ошибка при воспроизведении 2.4. Тесты: CacheManager был отключен. Он больше не может использоваться

Кэш очень сильно используется в нашем приложении. Мы используем 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) 
    } 
    } 
} 

Мы используем встроенные 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$1(Cache.scala:181): 
[error] Binding(interface net.sf.ehcache.Ehcache qualified with QualifierInstance(@play.cache.NamedCache(value=play)) to Prov 
iderTarget([email protected])) (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([email protected])) (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 

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

Спасибо заранее.

+0

Эта проблема, как правило, из-за наличия двух приложений Play-то работает в то же время (что из-за глобального одноплодной природы Ehcache в вызывает проблемы, поскольку, когда одно приложение отключается, другое все равно будет использовать его.) Используя 'WithApplication' _should_ запускайте и закрывайте новое приложение для каждой спецификации, но я обнаружил, что некоторые плагины (или, скорее, модули сейчас) могут вызывать проблемы, если они влияют на жизненный цикл приложения. Вы не предоставляете достаточно информации, чтобы полностью диагностировать, но попытайтесь выяснить, как два (поддельные) приложения могут запускаться одновременно. – Mikesname

+0

Да Mikesname .. даже у меня тот же dbt, что, когда каждая тестовая спецификация запускается как одно новое приложение, кеш должен автоматически запускаться и закрываться для каждой спецификации.Но, как вы упомянули, одноэлементная природа объекта кэша в ehcachemodule может быть проблемой. Любые способы использования Guice DI для ввода экземпляра кэша во время выполнения .. и реализовал отдельный Fake Cache, как это было предложено @Steve ниже. Я отключил по умолчанию EhCacheModule и включил этот модуль Fake cache для тестовой среды. И мои тесты в настоящее время работают нормально :) – Bhavya

ответ

5

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

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 = {} 
} 

Override инъекции:

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

+0

Привет @Steve ... Thnq для ответа. Пробовав с помощью вышеупомянутого решения, предоставленного вами, я был поражен со следующей проблемой. Можете ли вы помочь мне в этой проблеме: http://stackoverflow.com/questions/31100534/can-we-use-google-guice-di-with-a-scala-object-instead-of-a-scala-class- в игре – Bhavya

+0

@bhavya извините, пропустил это. Я согласен с ответом - когда вы используете DI, объекты могут быть преобразованы в программные классы singleton. –

3

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

class MySpec extends Specification { 
    sequential 

    ... 
} 

Примечание

parallelExecution in Test := false 

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

1

Я думаю, что обход проверки кеша путем подделки кеша - это плохая практика. Это делает недействительными ваши тесты, потому что вы пытаетесь не использовать EhCache масштабируемым и распределенным способом. Гораздо лучше было бы реализовать интерфейс @Singleton, как описано здесь: https://stackoverflow.com/a/31835029/5736587

Благодаря https://stackoverflow.com/users/2145368/mon-calamari