2015-06-25 8 views
10

La nostra applicazione è basata su Riproduci 2.4 con Scala 2.11 e Akka. Il database utilizzato è MySQL.Errore con Play 2.4 Test: CacheManager è stato chiuso. Non può più essere utilizzato

La cache viene utilizzata intensamente nella nostra applicazione. Usiamo EhCache predefinito per la memorizzazione nella cache.

Il nostro codice di esempio frammento di:

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

Usiamo giochiamo integrato Specs2 per il test.

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("\"", "") 
    } 

Ma mentre unit test abbiamo spesso incontrano l'errore sotto.

[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 

Vi aspettiamo per aiuto su entrambi risolvere il problema sopra o modi per implementare una cache finto esclusivamente per il test.

Grazie in anticipo.

+0

Questo problema è di solito a causa di avere due applicazioni Giocare in qualche modo in esecuzione allo stesso tempo (che a causa della natura Singleton globale di EHCache causa problemi, dal momento che quando un'applicazione si arresta, l'altro la userà ancora.) Usando 'WithApplication' _should_ avvia e chiude una nuova app per ogni specifica, ma ho trovato che alcuni plugin (o meglio, i moduli ora) può causare problemi se influiscono sul ciclo di vita dell'app. Non fornisci informazioni sufficienti per una diagnosi completa, ma prova a scoprire come potrebbero funzionare contemporaneamente due applicazioni (false). – Mikesname

+0

Sì Mikesname .. anche io ho lo stesso dbt che quando ogni specifica di test viene eseguita come una singola nuova applicazione .. la cache dovrebbe automaticamente avviarsi e spegnersi per ogni specifica.Ma come hai menzionato la natura singleton dell'oggetto cache nell'ehcachemodule potrebbe essere il problema .. Qualsiasi modo in cui ho iniziato a usare Guice DI per iniettare l'istanza della cache in fase di esecuzione .. e implementato una cache falsa separata come suggerito da @Steve qui sotto. Ho disabilitato il default EhCacheModule e ho abilitato questo modulo di cache Fake per l'ambiente di test .. E ora i miei test stanno funzionando bene :) – Bhavya

risposta

5

Avevo un problema simile, quindi ho cancellato un'implementazione della cache e l'ho sostituita per i test.

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 l'iniezione:

class AbstractViewTest extends PlaySpecification { 

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

} 

Si può vedere come io uso questo su GitHub: https://github.com/schaloner/deadbolt-2-scala/blob/master/code/test/be/objectify/deadbolt/scala/views/AbstractViewTest.scala

+0

Ciao @Steve ... Thnq per la risposta .. Mentre provavo con la soluzione di cui sopra fornita da te, sono stato colpito con il seguente problema. Potete aiutarmi con questo problema: http://stackoverflow.com/questions/31100534/can-we-use-google-guice-di-with-a-scala-object-instead-of-a-scala-class- in-play – Bhavya

+0

@bhavya scusate, mancate questo. Sono d'accordo con la risposta data - quando si utilizza DI, gli oggetti possono essere convertiti in classi programmiche singleton. –

3

Un'altra soluzione potrebbe essere quella di chiamare il metodo sequential sul all'inizio di ogni prova.

class MySpec extends Specification { 
    sequential 

    ... 
} 

Nota

parallelExecution in Test := false 

deve essere impostato nel file build.sbt troppo.

Problemi correlati