I'm using for the first time the cache of Play! Scala 2.5. It works well except for the tests.
My test still pass since I don't need the cache but I get this error (and a lot of others telling the same thing):
Unable to provision, see the following errors:
1) Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists.
I understand the error but I didn't manage to implement my own version of the cache API (to mock it).
I tried to do what is told on the play mailing list but without success (there is some differences with Play! 2.4 since the module is dependency injected). Any help would be welcome.
Edit: what I have done (and it does not change anything):
My CacheApi version (just for the tests):
class MyCacheApi extends CacheApi {
lazy val cache = {
val manager = CacheManager.getInstance()
manager.addCacheIfAbsent("play")
manager.getCache("play")
}
def set(key: String, value: Any, expiration: Duration = Duration.Inf) = {}
def remove(key: String) = {}
def getOrElse[A: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => A): A = {
get[A](key).getOrElse {
val value = orElse
set(key, value, expiration)
value
}
}
def get[T: ClassTag](key: String): Option[T] = None
}
And in my tests, I use it like this:
lazy val appBuilder = new GuiceApplicationBuilder()
.in(Mode.Test)
.overrides(bind[CacheApi].to[MyCacheApi])
lazy val injector = appBuilder.injector()
lazy val cache = new MyCacheApi
lazy val facebookAPI = new FacebookAPI(cache)
But when I'm testing the functions of the class FacebookAPI
, the tests pass, but I still have a lot of error messages due to the fact that an EhCache instance with name 'play' already exists...
I finally found a solution.
I added in a test.conf file (in the conf folder):
play.cache.bindCaches = ["controller-cache", "document-cache"]
play.cache.createBoundCaches = false
And to make this conf file used in the test I just added in the setting part of my build.sbt the following line:
javaOptions in Test += "-Dconfig.resource=tests.conf"
Let me know if you need more details.
Having several tests building play application, the only way we found to solve this issue is :
So in the application.conf used for the tests, and only for tests, disable the default caching :
play.modules.disabled += "play.api.cache.ehcache.EhCacheModule"
Write an implementation of the cache with a map that extends AsyncCacheApi :
package utils
import akka.Done
import net.sf.ehcache.Element
import play.api.cache.AsyncCacheApi
import scala.concurrent.duration.Duration
import scala.concurrent.{ExecutionContext, Future}
import scala.reflect.ClassTag
class InMemoryCache (implicit ec : ExecutionContext) extends AsyncCacheApi {
val cache = scala.collection.mutable.Map[String, Element]()
def set(key: String, value: Any, expiration: Duration): Future[Done] = Future {
val element = new Element(key, value)
if (expiration == 0) element.setEternal(true)
element.setTimeToLive(expiration.toSeconds.toInt)
cache.put(key, element)
Done
}
def remove(key: String): Future[Done] = Future {
cache -= key
Done
}
def get[T: ClassTag](key: String): Future[Option[T]] = Future {
cache.get(key).map(_.getObjectValue).asInstanceOf[Option[T]]
}
def getOrElseUpdate[A: ClassTag](key: String, expiration: Duration)(orElse: => Future[A]): Future[A] = {
get[A](key).flatMap {
case Some(value) => Future.successful(value)
case None => orElse.flatMap(value => set(key, value, expiration).map(_ => value))
}
}
def removeAll(): Future[Done] = Future {
cache.clear()
Done
}
}
Then on your tests :
val application = new GuiceApplicationBuilder().
overrides(
bind[AsyncCacheApi].toInstance(new utils.InMemoryCache())
).build
Play.start(application)
Versions used : Play 2.6.15 & Scala 2.12.4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With