2015-06-25 8 views
10

Unsere Anwendung ist auf gebaut Spielen Sie 2,4 mit Scala 2.11 und Akka. Die verwendete Datenbank ist MySQL.Fehler bei Play 2.4 Tests: Der CacheManager wurde heruntergefahren. Es kann nicht mehr verwendet werden

Cache wird stark in unserer Anwendung verwendet. Wir verwenden Play standardmäßig EhCache für Caching.

Unser Beispielcode-Schnipsel:

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

Wir verwenden Spiele-eingebauten Specs2 zum Testen.

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

Aber während Komponententests begegnen wir oft die folgenden Fehler.

[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 

Wir freuen uns auf Hilfe entweder auf die obige Frage oder Möglichkeiten der Lösung eine Mock-Cache ausschließlich für die Prüfung zu implementieren.

Vielen Dank im Voraus.

+0

Dieses Problem ist in der Regel durch zwei Play-Anwendungen irgendwie zugleich läuft mit (was aufgrund EHCache des globalen Singletons Natur verursacht Probleme, da wenn eine Anwendung heruntergefahren wird, die andere sie immer noch benutzt.) Unter Verwendung von 'WithApplication' _sollte_ eine neue App für jede Spezifikation starten und herunterfahren, aber ich habe festgestellt, dass bestimmte Plugins (oder besser, Module jetzt) kann Probleme verursachen, wenn sie den Lebenszyklus der App beeinflussen. Sie stellen nicht genügend Informationen bereit, um eine vollständige Diagnose zu stellen, aber versuchen Sie herauszufinden, wie zwei (gefälschte) Anwendungen gleichzeitig ausgeführt werden können. – Mikesname

+0

Ja Mikesname .. sogar ich habe die gleiche dbt, dass, wenn jede Testspezifikation als eine einzige neue Anwendung läuft .. der Cache automatisch für jede Spezifikation starten und herunterfahren sollte.Aber wie du erwähntest, könnte die Singleton-Natur des Cache-Objekts im ehcachemodule das Problem sein. Wie ich anfing, Guice DI zu verwenden, um Cache-Instanz zur Laufzeit zu injizieren .. und einen separaten Fake Cache implementiert, wie von @Steve unten vorgeschlagen. Ich deaktivierte Standard EhCacheModule und aktivierte dieses gefälschte Zwischenspeichermodul für Testumgebung. Und meine Tests laufen jetzt gut :) – Bhavya

Antwort

5

Ich hatte ein ähnliches Problem, also drückte ich eine Cache-Implementierung aus und tauschte sie für die Tests ein.

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

Aufschalten der Injektion:

class AbstractViewTest extends PlaySpecification { 

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

} 

Sie können sehen, wie ich das auf GitHub: https://github.com/schaloner/deadbolt-2-scala/blob/master/code/test/be/objectify/deadbolt/scala/views/AbstractViewTest.scala

+0

Hi @Steve ... Thnq für die Antwort. Beim Versuchen mit der oben genannten Lösung, die von Ihnen zur Verfügung gestellt wurde, wurde ich geschlagen mit dem folgenden Problem. Können Sie mir bei diesem Problem helfen? Http://stackoverflow.com/questions/31100534/can-we-use-google-guice-di-with-a-scala-object-instead-a-scala-class- im Spiel – Bhavya

+0

@bhavya Entschuldigung, verpasste dies. Ich stimme der Antwort zu - wenn Sie DI verwenden, können Objekte in programmatische Singleton-Klassen konvertiert werden. –

3

Eine andere Lösung wäre, die sequential Verfahren am Beginn jedes Tests zu nennen.

class MySpec extends Specification { 
    sequential 

    ... 
} 

Hinweis

parallelExecution in Test := false 

Sollte auch in der build.sbt Datei eingestellt werden.