diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index f94f501e..047ffdd9 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -28,18 +28,11 @@ jobs:
       - name: Setup JDK
         uses: coursier/setup-action@v1.3.4
         with:
-          jvm: adoptium:1.8
-
-      - name: Test Code Style
-        run: |
-          sbt --client "+scalariformFormat; +test:scalariformFormat"
-          git diff --exit-code || (
-            echo "ERROR: Scalariform check failed, see differences above."
-            echo "To fix, format your sources using sbt scalariformFormat test:scalariformFormat before submitting a pull request."
-            false
-          )
+          jvm: adoptium:1.17
 
       - name: Build
         timeout-minutes: 10
+        env:
+          COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: |
-          sbt --client "+clean; +compile; +Test/compile; +test;"
+          sbt --client "+clean; +compile; +Test/compile; +coverage; +test; +coverageReport; +coveralls;"
diff --git a/.scalariform.conf b/.scalariform.conf
deleted file mode 100644
index adbb97d4..00000000
--- a/.scalariform.conf
+++ /dev/null
@@ -1,30 +0,0 @@
-#alignArguments=false
-#alignParameters=false
-alignSingleLineCaseStatements=true
-#alignSingleLineCaseStatements.maxArrowIndent=40
-#allowParamGroupsOnNewlines=false
-#compactControlReadability=false
-#compactStringConcatenation=false
-danglingCloseParenthesis=Force
-#doubleIndentClassDeclaration=false
-#doubleIndentConstructorArguments=false
-doubleIndentMethodDeclaration=true
-#firstArgumentOnNewline=Force
-#firstParameterOnNewline=Force
-#formatXml=true
-#indentLocalDefs=false
-#indentPackageBlocks=true
-#indentSpaces=2
-#indentWithTabs=false
-#multilineScaladocCommentsStartOnFirstLine=false
-newlineAtEndOfFile=true
-placeScaladocAsterisksBeneathSecondAsterisk=true
-#preserveSpaceBeforeArguments=false
-#rewriteArrowSymbols=false
-#singleCasePatternOnNewline=true
-#spaceBeforeColon=false
-#spaceBeforeContextColon=false
-#spaceInsideBrackets=false
-#spaceInsideParentheses=false
-spacesAroundMultiImports=false
-#spacesWithinPatternBinders=true
diff --git a/bin/config b/bin/config
deleted file mode 100644
index dbc113f3..00000000
--- a/bin/config
+++ /dev/null
@@ -1,23 +0,0 @@
-#! /bin/bash
-
-# config for CI scripts
-
-set -o pipefail
-
-# Travis uses a detached HEAD during builds
-# CURRENT_BRANCH is used when updating Whitesource to determine the correct project name
-export CURRENT_BRANCH=${TRAVIS_BRANCH}
-
-# Command to run tests based on current branch
-[[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == false ]] && SBT_CMD="coverageOn test coverageOff publish" || SBT_CMD="coverage test"
-export SBT_CMD
-
-runSbt() {
-  sbt "$@"
-}
-
-doAfterSuccess() {
-  if [ $1 -eq 0 ]; then
-    sbt ++$2 coverageReport coveralls
-  fi
-}
diff --git a/bin/test b/bin/test
deleted file mode 100755
index ac166e2d..00000000
--- a/bin/test
+++ /dev/null
@@ -1,13 +0,0 @@
-#! /bin/bash
-
-. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/config"
-
-SCALA_VERSION="$1"
-
-runSbt ++$SCALA_VERSION $SBT_CMD
-
-result=$?
-
-doAfterSuccess $result $SCALA_VERSION
-
-exit $result
diff --git a/bin/test-code-style b/bin/test-code-style
deleted file mode 100755
index b2a0debf..00000000
--- a/bin/test-code-style
+++ /dev/null
@@ -1,12 +0,0 @@
-#! /bin/bash
-
-. "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/config"
-
-runSbt  +scalariformFormat \
-        +test:scalariformFormat
-
-git diff --exit-code || (
-  echo "ERROR: Scalariform check failed, see differences above."
-  echo "To fix, format your sources using sbt scalariformFormat test:scalariformFormat before submitting a pull request."
-  false
-)
diff --git a/build.sbt b/build.sbt
index 3f67bd71..d25a5030 100644
--- a/build.sbt
+++ b/build.sbt
@@ -9,21 +9,23 @@ description := "Redis cache plugin for the Play framework 2"
 
 organization := "com.github.karelcemus"
 
-scalaVersion := "2.13.8"
+crossScalaVersions := Seq("2.13.12") //, "3.3.0"
 
-crossScalaVersions := Seq("2.12.15", scalaVersion.value)
+scalaVersion := crossScalaVersions.value.head
 
-playVersion := "2.8.13"
+playVersion := "2.9.0"
 
 libraryDependencies ++= Seq(
   // play framework cache API
   "com.typesafe.play" %% "play-cache" % playVersion.value % Provided,
   // redis connector
-  "com.github.karelcemus" %% "rediscala" % "1.9.1",
+  "io.github.rediscala" %% "rediscala" % "1.14.0-akka",
   // test framework with mockito extension
-  "org.specs2" %% "specs2-mock" % "4.13.2" % Test,
+  "org.specs2" %% "specs2-mock" % "4.20.3" % Test,
   // test module for play framework
-  "com.typesafe.play" %% "play-specs2" % playVersion.value % Test
+  "com.typesafe.play" %% "play-test" % playVersion.value % Test,
+  // to run integration tests
+  "com.dimafeng" %% "testcontainers-scala-core" % "0.41.0" % Test
 )
 
 resolvers ++= Seq(
@@ -34,10 +36,6 @@ javacOptions ++= Seq("-Xlint:unchecked", "-encoding", "UTF-8")
 
 scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked")
 
-homepage := Some(url("https://github.com/karelcemus/play-redis"))
-
-licenses := Seq("Apache 2" -> url("https://www.apache.org/licenses/LICENSE-2.0"))
-
 enablePlugins(CustomReleasePlugin)
 
 // exclude from tests coverage
diff --git a/project/CustomReleasePlugin.scala b/project/CustomReleasePlugin.scala
index 0c077d08..d73b395e 100644
--- a/project/CustomReleasePlugin.scala
+++ b/project/CustomReleasePlugin.scala
@@ -32,7 +32,9 @@ object CustomReleasePlugin extends AutoPlugin {
     pomIncludeRepository := { _ => false },
     // customized release process
     releaseProcess := customizedReleaseProcess,
-    //
+    // release details
+    homepage := Some(url("https://github.com/karelcemus/play-redis")),
+    licenses := Seq("Apache 2" -> url("https://www.apache.org/licenses/LICENSE-2.0")),
     scmInfo := Some(
       ScmInfo(
         url("https://github.com/KarelCemus/play-i18n.git"),
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 0ebf6af2..81b0b226 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,14 +1,11 @@
 resolvers += Resolver.url("scoverage-bintray", url("https://dl.bintray.com/sksamuel/sbt-plugins/"))(Resolver.ivyStylePatterns)
 
 // checks for updates
-addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.1")
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
 
 // code coverage and uploader of the coverage results into the coveralls.io
-addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.8.2")
-addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.1")
-
-// code linter
-addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3")
+addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")
+addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.11")
 
 // library release
 addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1")
diff --git a/src/main/scala-2.12/play/api/cache/redis/JavaCompatibilityBase.scala b/src/main/scala-2.12/play/api/cache/redis/JavaCompatibilityBase.scala
deleted file mode 100644
index c333601f..00000000
--- a/src/main/scala-2.12/play/api/cache/redis/JavaCompatibilityBase.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package play.api.cache.redis
-
-import scala.collection.convert.{DecorateAsJava, DecorateAsScala}
-
-private[redis] trait JavaCompatibilityBase extends DecorateAsScala with DecorateAsJava
-
-private[redis] object JavaCompatibilityBase extends JavaCompatibilityBase
-
diff --git a/src/main/scala-2.13/play/api/cache/redis/JavaCompatibilityBase.scala b/src/main/scala/play/api/cache/redis/JavaCompatibilityBase.scala
similarity index 100%
rename from src/main/scala-2.13/play/api/cache/redis/JavaCompatibilityBase.scala
rename to src/main/scala/play/api/cache/redis/JavaCompatibilityBase.scala
diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala b/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala
index 60928212..dfde44e9 100644
--- a/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala
+++ b/src/main/scala/play/api/cache/redis/configuration/RedisHost.scala
@@ -14,6 +14,8 @@ trait RedisHost {
   def port: Int
   /** redis database identifier to work with */
   def database: Option[Int]
+  /** when enabled security, this returns username for the AUTH command */
+  def username: Option[String]
   /** when enabled security, this returns password for the AUTH command */
   def password: Option[String]
   // $COVERAGE-OFF$
@@ -21,13 +23,13 @@ trait RedisHost {
   override def equals(obj: scala.Any) = equalsAsHost(obj)
   /** trait-specific equals, invokable from children */
   protected def equalsAsHost(obj: scala.Any) = obj match {
-    case that: RedisHost => Equals.check(this, that)(_.host, _.port, _.database, _.password)
+    case that: RedisHost => Equals.check(this, that)(_.host, _.port, _.username, _.database, _.password)
     case _               => false
   }
   /** to string */
-  override def toString = (password, database) match {
-    case (Some(password), Some(database)) => s"redis://redis:$password@$host:$port?db=$database"
-    case (Some(password), None)           => s"redis://redis:$password@$host:$port"
+  override def toString: String = (password, database) match {
+    case (Some(password), Some(database)) => s"redis://${username.getOrElse("redis")}:$password@$host:$port?db=$database"
+    case (Some(password), None)           => s"redis://${username.getOrElse("redis")}:$password@$host:$port"
     case (None, Some(database))           => s"redis://$host:$port?db=$database"
     case (None, None)                     => s"redis://$host:$port"
   }
@@ -38,7 +40,7 @@ object RedisHost extends ConfigLoader[RedisHost] {
   import RedisConfigLoader._
 
   /** expected format of the environment variable */
-  private val ConnectionString = "redis://((?<user>[^:]+):(?<password>[^@]+)@)?(?<host>[^:]+):(?<port>[0-9]+)".r("auth", "user", "password", "host", "port")
+  private val ConnectionString = "redis://((?<username>[^:]+):(?<password>[^@]+)@)?(?<host>[^:]+):(?<port>[0-9]+)".r("auth", "username", "password", "host", "port")
 
   def load(config: Config, path: String): RedisHost = apply(
     host = config.getString(path / "host"),
@@ -51,29 +53,31 @@ object RedisHost extends ConfigLoader[RedisHost] {
   def fromConnectionString(connectionString: String): RedisHost = ConnectionString findFirstMatchIn connectionString match {
     // read the environment variable and fill missing information from the local configuration file
     case Some(matcher) => new RedisHost {
-      val host = matcher.group("host")
-      val port = matcher.group("port").toInt
-      val database = None
-      val password = Option(matcher.group("password"))
+      override val host: String = matcher.group("host")
+      override val port: Int = matcher.group("port").toInt
+      override val database: Option[Nothing] = None
+      override val username: Option[String] = Option(matcher.group("username"))
+      override val password: Option[String] = Option(matcher.group("password"))
     }
     // unexpected format
     case None => throw new IllegalArgumentException(s"Unexpected format of the connection string: '$connectionString'. Expected format is 'redis://[user:password@]host:port'.")
   }
 
-  def apply(host: String, port: Int, database: Option[Int] = None, password: Option[String] = None): RedisHost =
-    create(host, port, database, password)
+  def apply(host: String, port: Int, database: Option[Int] = None, username: Option[String] = None, password: Option[String] = None): RedisHost =
+    create(host, port, database, username, password)
 
   /** hackish method to preserve nice names of parameters in apply */
-  @inline private def create(_host: String, _port: Int, _database: Option[Int], _password: Option[String]) = new RedisHost {
-    val host = _host
-    val port = _port
-    val database = _database
-    val password = _password
+  @inline private def create(_host: String, _port: Int, _database: Option[Int], _username: Option[String], _password: Option[String]) = new RedisHost {
+    override val host: String = _host
+    override val port: Int = _port
+    override val database: Option[Int] = _database
+    override val username: Option[String] = _username
+    override val password: Option[String] = _password
   }
 
   // $COVERAGE-OFF$
-  def unapply(host: RedisHost): Option[(String, Int, Option[Int], Option[String])] = {
-    Some((host.host, host.port, host.database, host.password))
+  def unapply(host: RedisHost): Option[(String, Int, Option[Int],Option[String], Option[String])] = {
+    Some((host.host, host.port, host.database, host.username, host.password))
   }
   // $COVERAGE-ON$
 }
@@ -84,8 +88,9 @@ object RedisHost extends ConfigLoader[RedisHost] {
   */
 trait RedisDelegatingHost extends RedisHost {
   def innerHost: RedisHost
-  def host = innerHost.host
-  def port = innerHost.port
-  def database = innerHost.database
-  def password = innerHost.password
+  override def host: String = innerHost.host
+  override def port: Int = innerHost.port
+  override def database: Option[Int] = innerHost.database
+  override def username: Option[String] = innerHost.username
+  override def password: Option[String] = innerHost.password
 }
diff --git a/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala b/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala
index 0df7c272..03c2f334 100644
--- a/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala
+++ b/src/main/scala/play/api/cache/redis/configuration/RedisInstance.scala
@@ -91,6 +91,7 @@ trait RedisSentinel extends RedisInstance {
 
   def sentinels: List[RedisHost]
   def masterGroup: String
+  def username: Option[String]
   def password: Option[String]
   def database: Option[Int]
 
@@ -103,23 +104,28 @@ trait RedisSentinel extends RedisInstance {
 
 object RedisSentinel {
 
-  def apply(name: String, masterGroup: String,
-      sentinels: List[RedisHost],
-      settings: RedisSettings,
-      password: Option[String] = None,
-      database: Option[Int] = None): RedisSentinel with RedisDelegatingSettings =
-    create(name, masterGroup, password, database, sentinels, settings)
+  def apply(
+    name: String,
+    masterGroup: String,
+    sentinels: List[RedisHost],
+    settings: RedisSettings,
+    username: Option[String] = None,
+    password: Option[String] = None,
+    database: Option[Int] = None
+  ): RedisSentinel with RedisDelegatingSettings =
+    create(name, masterGroup, username, password, database, sentinels, settings)
 
   @inline
-  private def create(_name: String, _masterGroup: String, _password: Option[String], _database: Option[Int],
+  private def create(_name: String, _masterGroup: String, _username: Option[String], _password: Option[String], _database: Option[Int],
       _sentinels: List[RedisHost], _settings: RedisSettings) =
     new RedisSentinel with RedisDelegatingSettings {
-      val name = _name
-      val masterGroup = _masterGroup
-      val password = _password
-      val database = _database
-      val sentinels = _sentinels
-      val settings = _settings
+      override val name: String = _name
+      override val masterGroup: String = _masterGroup
+      override val username: Option[String] = _username
+      override val password: Option[String] = _password
+      override val database: Option[Int] = _database
+      override val sentinels: List[RedisHost] = _sentinels
+      override val settings: RedisSettings = _settings
     }
 
 }
diff --git a/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala b/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala
index eaad4a90..4c2c0bc5 100644
--- a/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala
+++ b/src/main/scala/play/api/cache/redis/connector/RedisCommands.scala
@@ -62,6 +62,7 @@ private[connector] class RedisCommandsStandalone(configuration: RedisStandalone)
     host = host,
     port = port,
     db = database,
+    username = username,
     password = password
   ) with FailEagerly with RedisRequestTimeout {
 
@@ -105,7 +106,7 @@ private[connector] class RedisCommandsCluster(configuration: RedisCluster)(impli
 
   val client: RedisClusterClient = new RedisClusterClient(
     nodes.map {
-      case RedisHost(host, port, database, password) => RedisServer(host.resolvedIpAddress, port, password, database)
+      case RedisHost(host, port, database, username, password) => RedisServer(host.resolvedIpAddress, port, username, password, database)
     }
   ) with RedisRequestTimeout {
     protected val timeout = configuration.timeout.redis
@@ -116,8 +117,8 @@ private[connector] class RedisCommandsCluster(configuration: RedisCluster)(impli
   // $COVERAGE-OFF$
   def start() = {
     def servers = nodes.map {
-      case RedisHost(host, port, Some(database), _) => s" $host:$port?database=$database"
-      case RedisHost(host, port, None, _)           => s" $host:$port"
+      case RedisHost(host, port, Some(database), _, _) => s" $host:$port?database=$database"
+      case RedisHost(host, port, None, _, _)           => s" $host:$port"
     }
 
     log.info(s"Redis cluster cache actor started. It is connected to ${servers mkString ", "}")
@@ -143,9 +144,10 @@ private[connector] class RedisCommandsSentinel(configuration: RedisSentinel)(imp
 
   val client: SentinelMonitoredRedisClient with RedisRequestTimeout = new SentinelMonitoredRedisClient(
     configuration.sentinels.map {
-      case RedisHost(host, port, _, _) => (host.resolvedIpAddress, port)
+      case RedisHost(host, port, _, _, _) => (host.resolvedIpAddress, port)
     },
     master = configuration.masterGroup,
+    username = configuration.username,
     password = configuration.password,
     db = configuration.database
   ) with RedisRequestTimeout {
diff --git a/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala b/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala
index ca1b9ce4..e1989f9b 100644
--- a/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala
+++ b/src/main/scala/play/api/cache/redis/impl/RedisCaches.scala
@@ -1,12 +1,11 @@
 package play.api.cache.redis.impl
 
 import javax.inject.Provider
-
 import play.api.Environment
 import play.api.cache.redis._
 import play.api.inject.ApplicationLifecycle
-
 import akka.actor.ActorSystem
+import play.api.cache.{AsyncCacheApi, DefaultSyncCacheApi}
 
 /**
   * Aggregates all available redis APIs into a single handler. This simplifies
@@ -27,16 +26,16 @@ private[redis] class RedisCachesProvider(instance: RedisInstance, serializer: co
 
   private implicit lazy val runtime: RedisRuntime = RedisRuntime(instance, instance.recovery, instance.invocationPolicy, instance.prefix)(system)
 
-  private implicit def implicitEnvironment = environment
+  private implicit def implicitEnvironment: Environment = environment
 
   lazy val get = new RedisCaches {
-    lazy val redisConnector = new connector.RedisConnectorProvider(instance, serializer).get
-    lazy val async = new AsyncRedisImpl(redisConnector)
-    lazy val sync = new SyncRedis(redisConnector)
-    lazy val scalaSync = new play.api.cache.DefaultSyncCacheApi(async)
-    lazy val scalaAsync = async
-    lazy val java = new AsyncJavaRedis(async)
-    lazy val javaAsync = java
-    lazy val javaSync = new play.cache.DefaultSyncCacheApi(java)
+    lazy val redisConnector: RedisConnector = new connector.RedisConnectorProvider(instance, serializer).get
+    lazy val async: AsyncRedis = new AsyncRedisImpl(redisConnector)
+    lazy val sync: CacheApi = new SyncRedis(redisConnector)
+    lazy val scalaSync: play.api.cache.SyncCacheApi = new play.api.cache.DefaultSyncCacheApi(async)
+    lazy val scalaAsync: play.api.cache.AsyncCacheApi = async
+    lazy val java: AsyncJavaRedis = new AsyncJavaRedis(async)
+    lazy val javaAsync: play.cache.redis.AsyncCacheApi = java
+    lazy val javaSync: play.cache.SyncCacheApi = new play.cache.DefaultSyncCacheApi(java)
   }
 }
diff --git a/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala
index dab7f8bd..f4a9b68a 100644
--- a/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala
+++ b/src/main/scala/play/api/cache/redis/impl/RedisListJavaImpl.scala
@@ -78,7 +78,7 @@ class RedisListJavaImpl[Elem](internal: RedisList[Elem, Future])(implicit runtim
     new AsyncRedisListModificationJavaImpl(internal.modify)
   }
 
-  class AsyncRedisListViewJavaImpl(view: internal.RedisListView) extends AsyncRedisList.AsyncRedisListView[Elem] {
+  private class AsyncRedisListViewJavaImpl(view: internal.RedisListView) extends AsyncRedisList.AsyncRedisListView[Elem] {
 
     def slice(from: Int, end: Int): CompletionStage[JavaList[Elem]] = {
       async { implicit context =>
@@ -87,7 +87,7 @@ class RedisListJavaImpl[Elem](internal: RedisList[Elem, Future])(implicit runtim
     }
   }
 
-  class AsyncRedisListModificationJavaImpl(modification: internal.RedisListModification) extends AsyncRedisList.AsyncRedisListModification[Elem] {
+  private class AsyncRedisListModificationJavaImpl(modification: internal.RedisListModification) extends AsyncRedisList.AsyncRedisListModification[Elem] {
 
     def collection(): AsyncRedisList[Elem] = This
 
diff --git a/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala b/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala
index 7304bb1c..f474311e 100644
--- a/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala
+++ b/src/main/scala/play/api/cache/redis/impl/RedisSetJavaImpl.scala
@@ -1,32 +1,32 @@
 package play.api.cache.redis.impl
 
-import scala.concurrent.Future
-
 import play.api.cache.redis.RedisSet
 import play.cache.redis.AsyncRedisSet
 
+import scala.concurrent.Future
+
 class RedisSetJavaImpl[Elem](internal: RedisSet[Elem, Future])(implicit runtime: RedisRuntime) extends AsyncRedisSet[Elem] {
   import JavaCompatibility._
 
-  def add(element: Elem*): CompletionStage[AsyncRedisSet[Elem]] = {
+  override def add(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = {
     async { implicit context =>
-      internal.add(element: _*).map(_ => this)
+      internal.add(elements: _*).map(_ => this)
     }
   }
 
-  def contains(element: Elem): CompletionStage[java.lang.Boolean] = {
+  override def contains(element: Elem): CompletionStage[java.lang.Boolean] = {
     async { implicit context =>
       internal.contains(element).map(Boolean.box)
     }
   }
 
-  def remove(element: Elem*): CompletionStage[AsyncRedisSet[Elem]] = {
+  override def remove(elements: Elem*): CompletionStage[AsyncRedisSet[Elem]] = {
     async { implicit context =>
-      internal.remove(element: _*).map(_ => this)
+      internal.remove(elements: _*).map(_ => this)
     }
   }
 
-  def toSet: CompletionStage[JavaSet[Elem]] = {
+  override def toSet: CompletionStage[JavaSet[Elem]] = {
     async { implicit context =>
       internal.toSet.map(_.asJava)
     }
diff --git a/src/test/scala-2.11/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala b/src/test/scala-2.11/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala
deleted file mode 100644
index 949012dc..00000000
--- a/src/test/scala-2.11/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala
+++ /dev/null
@@ -1,58 +0,0 @@
-package play.api.cache.redis.connector
-
-import java.util.Date
-
-import scala.reflect.ClassTag
-
-import play.api.inject.guice.GuiceApplicationBuilder
-import play.api.cache.redis._
-
-import org.joda.time.{DateTime, DateTimeZone}
-import org.specs2.mock.Mockito
-import org.specs2.mutable.Specification
-
-class ScalaSpecificSerializerSpec extends Specification with Mockito {
-  import SerializerImplicits._
-
-  private val system = GuiceApplicationBuilder().build().actorSystem
-
-  private implicit val serializer: AkkaSerializer = new AkkaSerializerImpl(system)
-
-  "AkkaEncoder" should "encode" >> {
-
-    "custom classes" in {
-      SimpleObject("B", 3).encoded mustEqual """
-          |rO0ABXNyAD9wbGF5LmFwaS5jYWNoZS5yZWRpcy5jb25uZWN0b3IuU2VyaWFsaXplckltcGxpY2l0
-          |cyRTaW1wbGVPYmplY3TbTGkeqUDNdwIAAkkABXZhbHVlTAADa2V5dAASTGphdmEvbGFuZy9TdHJp
-          |bmc7eHAAAAADdAABQg==
-        """.stripMargin.removeAllWhitespaces
-    }
-
-    "list" in {
-      List("A", "B", "C").encoded mustEqual """
-          |rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94
-          |eQAAAAAAAAABAwAAeHB0AAFBdAABQnQAAUNzcgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUu
-          |TGlzdFNlcmlhbGl6ZUVuZCSKXGNb91MLbQIAAHhweA==
-        """.stripMargin.removeAllWhitespaces
-    }
-  }
-
-  "AkkaDecoder" should "decode" >> {
-
-    "custom classes" in {
-      """
-        |rO0ABXNyAD9wbGF5LmFwaS5jYWNoZS5yZWRpcy5jb25uZWN0b3IuU2VyaWFsaXplckltcGxpY2l0
-        |cyRTaW1wbGVPYmplY3TbTGkeqUDNdwIAAkkABXZhbHVlTAADa2V5dAASTGphdmEvbGFuZy9TdHJp
-        |bmc7eHAAAAADdAABQg==
-      """.stripMargin.removeAllWhitespaces.decoded[SimpleObject] mustEqual SimpleObject("B", 3)
-    }
-
-    "list" in {
-      """
-        |rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94
-        |eQAAAAAAAAABAwAAeHB0AAFBdAABQnQAAUNzcgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUu
-        |TGlzdFNlcmlhbGl6ZUVuZCSKXGNb91MLbQIAAHhweA==
-      """.stripMargin.removeAllWhitespaces.decoded[List[String]] mustEqual List("A", "B", "C")
-    }
-  }
-}
diff --git a/src/test/scala-2.12/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala b/src/test/scala-2.12/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala
deleted file mode 100644
index 0671b42c..00000000
--- a/src/test/scala-2.12/play/api/cache/redis/connector/ScalaSpecificSerializerSpec.scala
+++ /dev/null
@@ -1,58 +0,0 @@
-package play.api.cache.redis.connector
-
-import java.util.Date
-
-import scala.reflect.ClassTag
-
-import play.api.inject.guice.GuiceApplicationBuilder
-import play.api.cache.redis._
-
-import org.joda.time.{DateTime, DateTimeZone}
-import org.specs2.mock.Mockito
-import org.specs2.mutable.Specification
-
-class ScalaSpecificSerializerSpec extends Specification with Mockito {
-  import SerializerImplicits._
-
-  private val system = GuiceApplicationBuilder().build().actorSystem
-
-  private implicit val serializer: AkkaSerializer = new AkkaSerializerImpl(system)
-
-  "AkkaEncoder" should "encode" >> {
-
-    "custom classes" in {
-      SimpleObject("B", 3).encoded mustEqual """
-          |rO0ABXNyAD9wbGF5LmFwaS5jYWNoZS5yZWRpcy5jb25uZWN0b3IuU2VyaWFsaXplckltcGxpY2l0c
-          |yRTaW1wbGVPYmplY3TbTGkeqUDNdwIAAkkABXZhbHVlTAADa2V5dAASTGphdmEvbGFuZy9TdHJpbm
-          |c7eHAAAAADdAABQg==
-        """.stripMargin.removeAllWhitespaces
-    }
-
-    "list" in {
-      List("A", "B", "C").encoded mustEqual """
-          |rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94
-          |eQAAAAAAAAABAwAAeHB0AAFBdAABQnQAAUNzcgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUu
-          |TGlzdFNlcmlhbGl6ZUVuZCSKXGNb91MLbQIAAHhweA==
-        """.stripMargin.removeAllWhitespaces
-    }
-  }
-
-  "AkkaDecoder" should "decode" >> {
-
-    "custom classes" in {
-      """
-        |rO0ABXNyAD9wbGF5LmFwaS5jYWNoZS5yZWRpcy5jb25uZWN0b3IuU2VyaWFsaXplckltcGxpY2l0c
-        |yRTaW1wbGVPYmplY3TbTGkeqUDNdwIAAkkABXZhbHVlTAADa2V5dAASTGphdmEvbGFuZy9TdHJpbm
-        |c7eHAAAAADdAABQg==
-      """.stripMargin.removeAllWhitespaces.decoded[SimpleObject] mustEqual SimpleObject("B", 3)
-    }
-
-    "list" in {
-      """
-        |rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94
-        |eQAAAAAAAAABAwAAeHB0AAFBdAABQnQAAUNzcgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUu
-        |TGlzdFNlcmlhbGl6ZUVuZCSKXGNb91MLbQIAAHhweA==
-      """.stripMargin.removeAllWhitespaces.decoded[List[String]] mustEqual List("A", "B", "C")
-    }
-  }
-}
diff --git a/src/test/scala/play/api/cache/redis/ClusterRedisContainer.scala b/src/test/scala/play/api/cache/redis/ClusterRedisContainer.scala
new file mode 100644
index 00000000..da359612
--- /dev/null
+++ b/src/test/scala/play/api/cache/redis/ClusterRedisContainer.scala
@@ -0,0 +1,20 @@
+package play.api.cache.redis
+
+trait ClusterRedisContainer extends RedisContainer {
+
+  protected def redisMaster = 4
+
+  protected def redisSlaves = 1
+
+  override protected lazy val redisConfig: RedisContainerConfig =
+    RedisContainerConfig(
+      "grokzen/redis-cluster:latest",
+      0.until(redisMaster * (redisSlaves + 1)).map(7000 + _),
+      Map(
+        "IP" -> "0.0.0.0",
+        "INITIAL_PORT" -> "7000",
+        "MASTERS" -> s"$redisMaster",
+        "SLAVES_PER_MASTER" -> s"$redisSlaves",
+      ),
+    )
+}
diff --git a/src/test/scala/play/api/cache/redis/ExpirationSpec.scala b/src/test/scala/play/api/cache/redis/ExpirationSpec.scala
index b546c6f1..8f160d79 100644
--- a/src/test/scala/play/api/cache/redis/ExpirationSpec.scala
+++ b/src/test/scala/play/api/cache/redis/ExpirationSpec.scala
@@ -1,12 +1,11 @@
 package play.api.cache.redis
 
-import java.util.Date
+import org.specs2.mutable.Specification
 
+import java.time.Instant
+import java.util.Date
 import scala.concurrent.duration._
 
-import org.joda.time._
-import org.specs2.mutable.Specification
-
 /**
   * <p>This specification tests expiration conversion</p>
   */
@@ -14,19 +13,19 @@ class ExpirationSpec extends Specification {
 
   "Expiration" should {
 
-    def expireAt = DateTime.now().plusMinutes(5).plusSeconds(30)
+    val expireAt: Instant = Instant.now().plusSeconds(5.minutes.toSeconds).plusSeconds(30)
 
     val expiration = 5.minutes + 30.seconds
     val expirationFrom = expiration - 2.second
     val expirationTo = expiration + 1.second
 
     "from java.util.Date" in {
-      new Date(expireAt.getMillis).asExpiration must beBetween(expirationFrom, expirationTo)
+      new Date(expireAt.toEpochMilli).asExpiration must beBetween(expirationFrom, expirationTo)
     }
 
     "from java.time.LocalDateTime" in {
       import java.time._
-      LocalDateTime.ofInstant(expireAt.toInstant.toDate.toInstant, ZoneId.systemDefault()).asExpiration must beBetween(expirationFrom, expirationTo)
+      LocalDateTime.ofInstant(expireAt, ZoneId.systemDefault()).asExpiration must beBetween(expirationFrom, expirationTo)
     }
   }
 }
diff --git a/src/test/scala/play/api/cache/redis/ForAllTestContainer.scala b/src/test/scala/play/api/cache/redis/ForAllTestContainer.scala
new file mode 100644
index 00000000..224f0a7c
--- /dev/null
+++ b/src/test/scala/play/api/cache/redis/ForAllTestContainer.scala
@@ -0,0 +1,19 @@
+package play.api.cache.redis
+
+import com.dimafeng.testcontainers.SingleContainer
+import org.specs2.specification.BeforeAfterAll
+
+trait ForAllTestContainer extends BeforeAfterAll {
+
+  def newContainer: SingleContainer[_]
+
+  final protected lazy val container = newContainer
+
+  override def beforeAll(): Unit = {
+    container.start()
+  }
+
+  override def afterAll(): Unit = {
+    container.stop()
+  }
+}
diff --git a/src/test/scala/play/api/cache/redis/Implicits.scala b/src/test/scala/play/api/cache/redis/Implicits.scala
index 671c37b3..abd74bf5 100644
--- a/src/test/scala/play/api/cache/redis/Implicits.scala
+++ b/src/test/scala/play/api/cache/redis/Implicits.scala
@@ -42,7 +42,7 @@ object Implicits {
   implicit def implicitlyAny2failure(ex: Throwable): Try[Nothing] = Failure(ex)
 
   implicit class FutureAwait[T](val future: Future[T]) extends AnyVal {
-    def await = Await.result(future, 2.minutes)
+    def awaitForFuture = Await.result(future, 2.minutes)
   }
 
   implicit def implicitlyAny2Callable[T](f: => T): Callable[T] = new Callable[T] {
@@ -73,9 +73,12 @@ trait ReducedMockito extends MocksCreation
   with MockitoStubs
   with CapturedArgument
   // with MockitoMatchers
-  with ArgThat
+  // with ArgThat
   with Expectations
-  with MockitoFunctions
+  with MockitoFunctions {
+
+  override def argThat[T, U <: T](m: org.specs2.matcher.Matcher[U]): T = super.argThat(m)
+}
 
 object MockitoImplicits extends ReducedMockito
 
@@ -85,13 +88,13 @@ trait WithApplication {
 
   protected def builder = new GuiceApplicationBuilder()
 
-  private val theBuilder = builder
+  private lazy val theBuilder = builder
 
-  protected val injector = theBuilder.injector
+  protected lazy val injector = theBuilder.injector()
 
-  protected val application: Application = injector.instanceOf[Application]
+  protected lazy val application: Application = injector.instanceOf[Application]
 
-  implicit protected val system = injector.instanceOf[ActorSystem]
+  implicit protected lazy val system: ActorSystem = injector.instanceOf[ActorSystem]
 }
 
 trait WithHocon {
@@ -101,13 +104,13 @@ trait WithHocon {
 
   protected def hocon: String
 
-  protected val config = {
+  protected lazy val config = {
     val reference = ConfigFactory.load()
     val local = ConfigFactory.parseString(hocon.stripMargin)
     local.withFallback(reference)
   }
 
-  protected val configuration = Configuration(config)
+  protected lazy val configuration = Configuration(config)
 }
 
 abstract class WithConfiguration(val hocon: String) extends WithHocon with Around with Scope {
diff --git a/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala b/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala
index cd748a9a..172f089a 100644
--- a/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala
+++ b/src/test/scala/play/api/cache/redis/RecoveryPolicySpec.scala
@@ -5,10 +5,9 @@ import scala.concurrent.Future
 import play.api.Logger
 
 import org.specs2.concurrent.ExecutionEnv
-import org.specs2.mock.Mockito
 import org.specs2.mutable.Specification
 
-class RecoveryPolicySpec(implicit ee: ExecutionEnv) extends Specification with Mockito {
+class RecoveryPolicySpec(implicit ee: ExecutionEnv) extends Specification with ReducedMockito {
 
   class BasicPolicy extends RecoveryPolicy {
     def recoverFrom[T](rerun: => Future[T], default: => Future[T], failure: RedisException) = default
@@ -25,7 +24,7 @@ class RecoveryPolicySpec(implicit ee: ExecutionEnv) extends Specification with M
     val failedKey = ExecutionFailedException(Some("key"), "TEST-CMD", "TEST-CMD key value", internal)
     val timeout = TimeoutException(internal)
     val serialization = SerializationException("some key", "TEST-CMD", internal)
-    def any = unexpectedAny
+    def any: UnexpectedResponseException = unexpectedAny
   }
 
   "Recovery Policy" should {
diff --git a/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala b/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala
index 07258ed1..f8ca9019 100644
--- a/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala
+++ b/src/test/scala/play/api/cache/redis/RedisCacheComponentsSpec.scala
@@ -2,23 +2,23 @@ package play.api.cache.redis
 
 import play.api._
 import play.api.inject.ApplicationLifecycle
-
 import org.specs2.mutable.Specification
-import org.specs2.specification.AfterAll
 
-class RedisCacheComponentsSpec extends Specification with WithApplication with AfterAll {
+class RedisCacheComponentsSpec extends Specification with WithApplication with StandaloneRedisContainer {
+  import Implicits._
 
-  object components extends RedisCacheComponents {
+  object components extends RedisCacheComponents with WithHocon {
     def actorSystem = system
     def applicationLifecycle = injector.instanceOf[ApplicationLifecycle]
     def environment = injector.instanceOf[Environment]
-    def configuration = injector.instanceOf[Configuration]
-    val syncRedis = cacheApi("play").sync
+    lazy val syncRedis = cacheApi("play").sync
+    override lazy val configuration = Configuration(config)
+    override protected def hocon: String = s"play.cache.redis.port: ${container.mappedPort(defaultPort)}"
   }
 
   private type Cache = CacheApi
 
-  private val cache = components.syncRedis
+  private lazy val cache = components.syncRedis
 
   val prefix = "components-sync"
 
@@ -42,7 +42,8 @@ class RedisCacheComponentsSpec extends Specification with WithApplication with A
     }
   }
 
-  def afterAll() = {
-    Shutdown.run
+  override def afterAll() = {
+    Shutdown.run.awaitForFuture
+    super.afterAll()
   }
 }
diff --git a/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala b/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala
index 13db7d7a..8735aa78 100644
--- a/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala
+++ b/src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala
@@ -163,16 +163,17 @@ object RedisCacheModuleSpec {
 
   object MyRedisInstance extends RedisStandalone {
 
-    def name = defaultCacheName
-    def invocationContext = "akka.actor.default-dispatcher"
-    def invocationPolicy = "lazy"
-    def timeout = RedisTimeouts(1.second)
-    def recovery = "log-and-default"
-    def source = "my-instance"
-    def prefix = None
-    def host = localhost
-    def port = defaultPort
-    def database = None
-    def password = None
+    override def name = defaultCacheName
+    override def invocationContext = "akka.actor.default-dispatcher"
+    override def invocationPolicy = "lazy"
+    override def timeout = RedisTimeouts(1.second)
+    override def recovery = "log-and-default"
+    override def source = "my-instance"
+    override def prefix = None
+    override def host = localhost
+    override def port = defaultPort
+    override def database = None
+    override def username = None
+    override def password = None
   }
 }
diff --git a/src/test/scala/play/api/cache/redis/RedisContainer.scala b/src/test/scala/play/api/cache/redis/RedisContainer.scala
new file mode 100644
index 00000000..b0682d46
--- /dev/null
+++ b/src/test/scala/play/api/cache/redis/RedisContainer.scala
@@ -0,0 +1,19 @@
+package play.api.cache.redis
+
+import com.dimafeng.testcontainers.GenericContainer
+import org.testcontainers.containers.wait.strategy.Wait
+
+trait RedisContainer extends ForAllTestContainer {
+
+  protected def redisConfig: RedisContainerConfig
+
+  private lazy val config = redisConfig
+
+  override val newContainer = GenericContainer(
+    dockerImage = config.redisDockerImage,
+    exposedPorts = config.redisPorts,
+    env = config.redisEnvironment,
+    waitStrategy = Wait.forListeningPorts(config.redisPorts: _*),
+  )
+}
+
diff --git a/src/test/scala/play/api/cache/redis/RedisContainerConfig.scala b/src/test/scala/play/api/cache/redis/RedisContainerConfig.scala
new file mode 100644
index 00000000..f5cdb544
--- /dev/null
+++ b/src/test/scala/play/api/cache/redis/RedisContainerConfig.scala
@@ -0,0 +1,7 @@
+package play.api.cache.redis
+
+final case class RedisContainerConfig(
+  redisDockerImage: String,
+  redisPorts: Seq[Int],
+  redisEnvironment: Map[String, String],
+)
diff --git a/src/test/scala/play/api/cache/redis/StandaloneRedisContainer.scala b/src/test/scala/play/api/cache/redis/StandaloneRedisContainer.scala
new file mode 100644
index 00000000..505e0276
--- /dev/null
+++ b/src/test/scala/play/api/cache/redis/StandaloneRedisContainer.scala
@@ -0,0 +1,11 @@
+package play.api.cache.redis
+
+trait StandaloneRedisContainer extends RedisContainer {
+
+  override protected lazy val redisConfig: RedisContainerConfig =
+    RedisContainerConfig(
+      redisDockerImage = "redis:latest",
+      redisPorts = Seq(6379),
+      redisEnvironment = Map.empty
+    )
+}
diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala
index eebca540..312f7120 100644
--- a/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala
+++ b/src/test/scala/play/api/cache/redis/configuration/RedisHostSpec.scala
@@ -1,13 +1,13 @@
 package play.api.cache.redis.configuration
 
 import play.api.cache.redis._
-
 import org.specs2.mutable.Specification
+import play.api.ConfigLoader
 
 class RedisHostSpec extends Specification {
   import Implicits._
 
-  private implicit val loader = RedisHost
+  private implicit val loader: ConfigLoader[RedisHost] = RedisHost
 
   "host with database and password" in new WithConfiguration(
     """
@@ -34,7 +34,7 @@ class RedisHostSpec extends Specification {
   }
 
   "host from connection string" in {
-    RedisHost.fromConnectionString("redis://redis:something@localhost:6378") mustEqual RedisHost("localhost", 6378, password = "something")
+    RedisHost.fromConnectionString("redis://redis:something@localhost:6378") mustEqual RedisHost("localhost", 6378, username = "redis", password = "something")
     RedisHost.fromConnectionString("redis://localhost:6378") mustEqual RedisHost("localhost", 6378)
     // test invalid string
     RedisHost.fromConnectionString("redis:/localhost:6378") must throwA[IllegalArgumentException]
diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerTest.scala b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerTest.scala
index 8a6956ad..7c81c3dd 100644
--- a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerTest.scala
+++ b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerTest.scala
@@ -1,5 +1,6 @@
 package play.api.cache.redis.configuration
 
+import play.api.ConfigLoader
 import play.api.cache.redis._
 
 /**
@@ -18,7 +19,7 @@ case class RedisInstanceManagerTest(default: String)(providers: RedisInstancePro
 
 abstract class WithRedisInstanceManager(hocon: String) extends WithConfiguration(hocon) {
 
-  private implicit val loader = RedisInstanceManager
+  private implicit val loader: ConfigLoader[RedisInstanceManager] = RedisInstanceManager
 
   protected val manager = configuration.get[RedisInstanceManager]("play.cache.redis")
 }
diff --git a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala
index 71a47652..0a9e016e 100644
--- a/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala
+++ b/src/test/scala/play/api/cache/redis/configuration/RedisInstanceProviderSpec.scala
@@ -7,7 +7,7 @@ class RedisInstanceProviderSpec extends Specification {
 
   val defaultCache = RedisStandalone(defaultCacheName, RedisHost(localhost, defaultPort, database = 0), defaults)
 
-  implicit val resolver = new RedisInstanceResolver {
+  implicit val resolver: RedisInstanceResolver = new RedisInstanceResolver {
     def resolve = {
       case `defaultCacheName` => defaultCache
     }
diff --git a/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala
index 066afa59..3c3b8f79 100644
--- a/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala
+++ b/src/test/scala/play/api/cache/redis/connector/RedisClusterSpec.scala
@@ -15,19 +15,31 @@ import org.specs2.specification.{AfterAll, BeforeAll}
 /**
   * <p>Specification of the low level connector implementing basic commands</p>
   */
-class RedisClusterSpec(implicit ee: ExecutionEnv) extends Specification with BeforeAll with AfterAll with WithApplication {
+class RedisClusterSpec(implicit ee: ExecutionEnv) extends Specification with WithApplication with ClusterRedisContainer {
+
+  args(skipAll=true)
 
   import Implicits._
 
-  implicit private val lifecycle = application.injector.instanceOf[ApplicationLifecycle]
+  implicit private val lifecycle: ApplicationLifecycle = application.injector.instanceOf[ApplicationLifecycle]
 
-  implicit private val runtime = RedisRuntime("cluster", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
+  implicit private val runtime: RedisRuntime = RedisRuntime("cluster", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
 
   private val serializer = new AkkaSerializerImpl(system)
 
-  private val clusterInstance = RedisCluster(defaultCacheName, nodes = RedisHost(dockerIp, 7000) :: RedisHost(dockerIp, 7001) :: RedisHost(dockerIp, 7002) :: RedisHost(dockerIp, 7003) :: Nil, defaults)
+  private lazy val containerIpAddress = container.containerIpAddress
+
+  private lazy val clusterInstance = RedisCluster(
+    name = defaultCacheName,
+    nodes = RedisHost(containerIpAddress, container.mappedPort(7000)) ::
+      RedisHost(containerIpAddress, container.mappedPort(7001)) ::
+      RedisHost(containerIpAddress, container.mappedPort(7002)) ::
+      RedisHost(containerIpAddress, container.mappedPort(7003)) ::
+      Nil,
+    settings = defaults
+  )
 
-  private val connector: RedisConnector = new RedisConnectorProvider(clusterInstance, serializer).get
+  private lazy val connector: RedisConnector = new RedisConnectorProvider(clusterInstance, serializer).get
 
   val prefix = "cluster-test"
 
@@ -71,12 +83,16 @@ class RedisClusterSpec(implicit ee: ExecutionEnv) extends Specification with Bef
     }
   }
 
-  def beforeAll() = {
+  override def beforeAll() = {
+    super.beforeAll()
     // initialize the connector by flushing the database
-    connector.matching(s"$prefix-*").flatMap(connector.remove).await
+    connector.matching(s"$prefix-*").flatMap {
+      keys => Future.sequence(keys.map(connector.remove(_)))
+    }.awaitForFuture
   }
 
-  def afterAll() = {
-    Shutdown.run
+  override def afterAll() = {
+    Shutdown.run.awaitForFuture
+    super.afterAll()
   }
 }
diff --git a/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala
index a1c10fa4..6a2221d0 100644
--- a/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala
+++ b/src/test/scala/play/api/cache/redis/connector/RedisConnectorFailureSpec.scala
@@ -40,7 +40,7 @@ class RedisConnectorFailureSpec(implicit ee: ExecutionEnv) extends Specification
     }
 
     "fail when decoder fails" in new MockedConnector {
-      serializer.decode(anyString)(any[ClassTag[_]]) returns simulatedFailure
+      serializer.decode(anyString)(any()) returns simulatedFailure
       commands.get[String](key) returns someValue
       // run the test
       connector.get[String](key) must throwA[SerializationException].await
diff --git a/src/test/scala/play/api/cache/redis/connector/RedisConnectorSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisConnectorSpec.scala
index 48f2be72..dcbfc156 100644
--- a/src/test/scala/play/api/cache/redis/connector/RedisConnectorSpec.scala
+++ b/src/test/scala/play/api/cache/redis/connector/RedisConnectorSpec.scala
@@ -1,29 +1,31 @@
 package play.api.cache.redis.connector
 
-import scala.concurrent.duration._
-import scala.concurrent.{ExecutionContext, Future}
-
+import org.specs2.concurrent.ExecutionEnv
+import org.specs2.mutable.Specification
 import play.api.cache.redis._
+import play.api.cache.redis.configuration.{RedisHost, RedisStandalone}
 import play.api.cache.redis.impl._
 import play.api.inject.ApplicationLifecycle
 
-import org.specs2.concurrent.ExecutionEnv
-import org.specs2.mutable.Specification
-import org.specs2.specification.{AfterAll, BeforeAll}
+import scala.concurrent.duration._
+import scala.concurrent.{ExecutionContext, Future}
 
 /**
   * <p>Specification of the low level connector implementing basic commands</p>
   */
-class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with BeforeAll with AfterAll with WithApplication {
+class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with WithApplication with StandaloneRedisContainer {
   import Implicits._
 
-  implicit private val lifecycle = application.injector.instanceOf[ApplicationLifecycle]
+  implicit private val lifecycle: ApplicationLifecycle = application.injector.instanceOf[ApplicationLifecycle]
 
-  implicit private val runtime = RedisRuntime("connector", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
+  implicit private val runtime: RedisRuntime = RedisRuntime("connector", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
 
   private val serializer = new AkkaSerializerImpl(system)
 
-  private val connector: RedisConnector = new RedisConnectorProvider(defaultInstance, serializer).get
+  private lazy val connector: RedisConnector = new RedisConnectorProvider(
+    RedisStandalone(defaultCacheName, RedisHost(container.containerIpAddress, container.mappedPort(defaultPort)), defaults),
+    serializer
+  ).get
 
   val prefix = "connector-test"
 
@@ -67,11 +69,11 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
     }
 
     "hit after mset" in new TestCase {
-      connector.mSet(s"$prefix-mset-$idx-1" -> "value-1", s"$prefix-mset-$idx-2" -> "value-2").await
+      connector.mSet(s"$prefix-mset-$idx-1" -> "value-1", s"$prefix-mset-$idx-2" -> "value-2").awaitForFuture
       connector.mGet[String](s"$prefix-mset-$idx-1", s"$prefix-mset-$idx-2", s"$prefix-mset-$idx-3") must beEqualTo(List(Some("value-1"), Some("value-2"), None)).await
-      connector.mSet(s"$prefix-mset-$idx-3" -> "value-3", s"$prefix-mset-$idx-2" -> null).await
+      connector.mSet(s"$prefix-mset-$idx-3" -> "value-3", s"$prefix-mset-$idx-2" -> null).awaitForFuture
       connector.mGet[String](s"$prefix-mset-$idx-1", s"$prefix-mset-$idx-2", s"$prefix-mset-$idx-3") must beEqualTo(List(Some("value-1"), None, Some("value-3"))).await
-      connector.mSet(s"$prefix-mset-$idx-3" -> null).await
+      connector.mSet(s"$prefix-mset-$idx-3" -> null).awaitForFuture
       connector.mGet[String](s"$prefix-mset-$idx-1", s"$prefix-mset-$idx-2", s"$prefix-mset-$idx-3") must beEqualTo(List(Some("value-1"), None, None)).await
     }
 
@@ -84,7 +86,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
     "expire refreshes expiration" in new TestCase {
       connector.set(s"$prefix-$idx", "value", 2.second).await
       connector.get[String](s"$prefix-$idx") must beSome("value").await
-      connector.expire(s"$prefix-$idx", 1.minute).await
+      connector.expire(s"$prefix-$idx", 1.minute).awaitForFuture
       // wait until the first duration expires
       Future.after(3) must not(throwA[Throwable]).awaitFor(4.seconds)
       connector.get[String](s"$prefix-$idx") must beSome("value").await
@@ -174,7 +176,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
       connector.get[String](s"$prefix-remove-multiple-2") must beSome[Any].await
       connector.set(s"$prefix-remove-multiple-3", "value").await
       connector.get[String](s"$prefix-remove-multiple-3") must beSome[Any].await
-      connector.remove(s"$prefix-remove-multiple-1", s"$prefix-remove-multiple-2", s"$prefix-remove-multiple-3").await
+      connector.remove(s"$prefix-remove-multiple-1", s"$prefix-remove-multiple-2", s"$prefix-remove-multiple-3").awaitForFuture
       connector.get[String](s"$prefix-remove-multiple-1") must beNone.await
       connector.get[String](s"$prefix-remove-multiple-2") must beNone.await
       connector.get[String](s"$prefix-remove-multiple-3") must beNone.await
@@ -187,7 +189,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
       connector.get[String](s"$prefix-remove-batch-2") must beSome[Any].await
       connector.set(s"$prefix-remove-batch-3", "value").await
       connector.get[String](s"$prefix-remove-batch-3") must beSome[Any].await
-      connector.remove(s"$prefix-remove-batch-1", s"$prefix-remove-batch-2", s"$prefix-remove-batch-3").await
+      connector.remove(s"$prefix-remove-batch-1", s"$prefix-remove-batch-2", s"$prefix-remove-batch-3").awaitForFuture
       connector.get[String](s"$prefix-remove-batch-1") must beNone.await
       connector.get[String](s"$prefix-remove-batch-2") must beNone.await
       connector.get[String](s"$prefix-remove-batch-3") must beNone.await
@@ -235,14 +237,14 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
 
     "append like set when value is undefined" in new TestCase {
       connector.get[String](s"$prefix-append-to-null") must beNone.await
-      connector.append(s"$prefix-append-to-null", "value").await
+      connector.append(s"$prefix-append-to-null", "value").awaitForFuture
       connector.get[String](s"$prefix-append-to-null") must beSome("value").await
     }
 
     "append to existing string" in new TestCase {
       connector.set(s"$prefix-append-to-some", "some").await
       connector.get[String](s"$prefix-append-to-some") must beSome("some").await
-      connector.append(s"$prefix-append-to-some", " value").await
+      connector.append(s"$prefix-append-to-some", " value").awaitForFuture
       connector.get[String](s"$prefix-append-to-some") must beSome("some value").await
     }
 
@@ -266,7 +268,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
 
     "list overwrite at index" in new TestCase {
       connector.listPrepend(s"$prefix-list-set", "C", "B", "A") must beEqualTo(3).await
-      connector.listSetAt(s"$prefix-list-set", 1, "D").await
+      connector.listSetAt(s"$prefix-list-set", 1, "D").awaitForFuture
       connector.listSlice[String](s"$prefix-list-set", 0, -1) must beEqualTo(List("A", "D", "C")).await
       connector.listSetAt(s"$prefix-list-set", 3, "D") must throwA[IndexOutOfBoundsException].await
     }
@@ -297,7 +299,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
 
     "list trim" in new TestCase {
       connector.listPrepend(s"$prefix-list-trim", "C", "B", "A") must beEqualTo(3).await
-      connector.listTrim(s"$prefix-list-trim", 1, 2).await
+      connector.listTrim(s"$prefix-list-trim", 1, 2).awaitForFuture
       connector.listSize(s"$prefix-list-trim") must beEqualTo(2).await
       connector.listSlice[String](s"$prefix-list-trim", 0, -1) must beEqualTo(List("B", "C")).await
     }
@@ -380,7 +382,7 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
       val key = s"$prefix-hash-set"
 
       connector.hashSize(key) must beEqualTo(0).await
-      connector.hashGetAll(key) must beEqualTo(Map.empty).await
+      connector.hashGetAll[String](key) must beEqualTo(Map.empty).await
       connector.hashKeys(key) must beEqualTo(Set.empty).await
       connector.hashValues[String](key) must beEqualTo(Set.empty).await
 
@@ -496,12 +498,14 @@ class RedisConnectorSpec(implicit ee: ExecutionEnv) extends Specification with B
     }
   }
 
-  def beforeAll() = {
+  override def beforeAll(): Unit = {
+    super.beforeAll()
     // initialize the connector by flushing the database
-    connector.matching(s"$prefix-*").flatMap(connector.remove).await
+    connector.matching(s"$prefix-*").flatMap(connector.remove).awaitForFuture
   }
 
-  def afterAll() = {
-    Shutdown.run
+  override def afterAll(): Unit = {
+    Shutdown.run.awaitForFuture
+    super.afterAll()
   }
 }
diff --git a/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala b/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala
index 92471e05..6532f101 100644
--- a/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala
+++ b/src/test/scala/play/api/cache/redis/connector/RedisSentinelSpec.scala
@@ -16,17 +16,19 @@ import scala.concurrent.{ExecutionContext, Future}
   */
 class RedisSentinelSpec(implicit ee: ExecutionEnv) extends Specification with BeforeAll with AfterAll with WithApplication {
 
+  args(skipAll=true)
+
   import Implicits._
 
-  implicit private val lifecycle = application.injector.instanceOf[ApplicationLifecycle]
+  implicit private val lifecycle: ApplicationLifecycle = application.injector.instanceOf[ApplicationLifecycle]
 
-  implicit private val runtime = RedisRuntime("sentinel", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
+  implicit private val runtime: RedisRuntime = RedisRuntime("sentinel", syncTimeout = 5.seconds, ExecutionContext.global, new LogAndFailPolicy, LazyInvocation)
 
   private val serializer = new AkkaSerializerImpl(system)
 
-  private val sentinelInstance = RedisSentinel(defaultCacheName, masterGroup = "sentinel5000", sentinels = RedisHost(dockerIp, 5000) :: RedisHost(dockerIp, 5001) :: RedisHost(dockerIp, 5002) :: Nil, defaults)
+  private lazy val sentinelInstance = RedisSentinel(defaultCacheName, masterGroup = "sentinel5000", sentinels = RedisHost(dockerIp, 5000) :: RedisHost(dockerIp, 5001) :: RedisHost(dockerIp, 5002) :: Nil, defaults)
 
-  private val connector: RedisConnector = new RedisConnectorProvider(sentinelInstance, serializer).get
+  private lazy val connector: RedisConnector = new RedisConnectorProvider(sentinelInstance, serializer).get
 
   val prefix = "sentinel-test"
 
@@ -72,7 +74,7 @@ class RedisSentinelSpec(implicit ee: ExecutionEnv) extends Specification with Be
 
   def beforeAll(): Unit = {
     // initialize the connector by flushing the database
-    connector.matching(s"$prefix-*").flatMap(connector.remove).await
+    connector.matching(s"$prefix-*").flatMap(connector.remove).awaitForFuture
   }
 
   def afterAll(): Unit = {
diff --git a/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala b/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala
index 77cd047b..a148cf35 100644
--- a/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala
+++ b/src/test/scala/play/api/cache/redis/connector/SerializerSpec.scala
@@ -2,12 +2,9 @@ package play.api.cache.redis.connector
 
 import java.util.Date
 
-import scala.reflect.ClassTag
-
 import play.api.inject.guice.GuiceApplicationBuilder
 import play.api.cache.redis._
 
-import org.joda.time.{DateTime, DateTimeZone}
 import org.specs2.mock.Mockito
 import org.specs2.mutable.Specification
 
@@ -67,16 +64,6 @@ class SerializerSpec extends Specification with Mockito {
       new Date(123).encoded mustEqual "rO0ABXNyAA5qYXZhLnV0aWwuRGF0ZWhqgQFLWXQZAwAAeHB3CAAAAAAAAAB7eA=="
     }
 
-    "datetime" in {
-      new DateTime(123456L, DateTimeZone.forID("UTC")).encoded mustEqual """
-          |rO0ABXNyABZvcmcuam9kYS50aW1lLkRhdGVUaW1luDx4ZGpb3fkCAAB4cgAfb3JnLmpvZGEudGlt
-          |ZS5iYXNlLkJhc2VEYXRlVGltZf//+eFPXS6jAgACSgAHaU1pbGxpc0wAC2lDaHJvbm9sb2d5dAAa
-          |TG9yZy9qb2RhL3RpbWUvQ2hyb25vbG9neTt4cAAAAAAAAeJAc3IAJ29yZy5qb2RhLnRpbWUuY2hy
-          |b25vLklTT0Nocm9ub2xvZ3kkU3R1YqnIEWZxN1AnAwAAeHBzcgAfb3JnLmpvZGEudGltZS5EYXRl
-          |VGltZVpvbmUkU3R1YqYvAZp8MhrjAwAAeHB3BQADVVRDeHg=
-        """.stripMargin.removeAllWhitespaces
-    }
-
     "null" in {
       new ValueEncoder(null).encoded must throwA[UnsupportedOperationException]
     }
@@ -134,18 +121,8 @@ class SerializerSpec extends Specification with Mockito {
       "rO0ABXNyAA5qYXZhLnV0aWwuRGF0ZWhqgQFLWXQZAwAAeHB3CAAAAAAAAAB7eA==".decoded[Date] mustEqual new Date(123)
     }
 
-    "datetime" in {
-      """
-        |rO0ABXNyABZvcmcuam9kYS50aW1lLkRhdGVUaW1luDx4ZGpb3fkCAAB4cgAfb3JnLmpvZGEudGlt
-        |ZS5iYXNlLkJhc2VEYXRlVGltZf//+eFPXS6jAgACSgAHaU1pbGxpc0wAC2lDaHJvbm9sb2d5dAAa
-        |TG9yZy9qb2RhL3RpbWUvQ2hyb25vbG9neTt4cAAAAAAAAeJAc3IAJ29yZy5qb2RhLnRpbWUuY2hy
-        |b25vLklTT0Nocm9ub2xvZ3kkU3R1YqnIEWZxN1AnAwAAeHBzcgAfb3JnLmpvZGEudGltZS5EYXRl
-        |VGltZVpvbmUkU3R1YqYvAZp8MhrjAwAAeHB3BQADVVRDeHg=
-      """.stripMargin.removeAllWhitespaces.decoded[DateTime] mustEqual new DateTime(123456L, DateTimeZone.forID("UTC"))
-    }
-
-    "forgotten type" in {
-      def decoded: String = "something".decoded
+    "invalid type" in {
+      def decoded: Date = "something".decoded[Date]
       decoded must throwA[IllegalArgumentException]
     }
   }
diff --git a/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala b/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala
index add0174b..d98d4311 100644
--- a/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala
+++ b/src/test/scala/play/api/cache/redis/impl/AsyncRedisSpec.scala
@@ -42,7 +42,7 @@ class AsyncRedisSpec(implicit ee: ExecutionEnv) extends Specification with Reduc
 
     "getOrElseUpdate (failing orElse)" in new MockedAsyncRedis with OrElse {
       connector.get[String](anyString)(anyClassTag) returns None
-      cache.getOrElseUpdate(key)(failedFuture) must throwA[TimeoutException].await
+      cache.getOrElseUpdate[String](key)(failedFuture) must throwA[TimeoutException].await
       orElse mustEqual 2
     }
 
diff --git a/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala b/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala
index 829439c9..7ea241d5 100644
--- a/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala
+++ b/src/test/scala/play/api/cache/redis/impl/BuildersSpec.scala
@@ -11,7 +11,7 @@ import org.specs2.mock.Mockito
 import org.specs2.mutable.Specification
 import org.specs2.specification._
 
-class BuildersSpec(implicit ee: ExecutionEnv) extends Specification with Mockito with WithApplication {
+class BuildersSpec(implicit ee: ExecutionEnv) extends Specification with ReducedMockito with WithApplication {
 
   import Builders._
   import BuildersSpec._
diff --git a/src/test/scala/play/api/cache/redis/impl/RedisCacheImplicits.scala b/src/test/scala/play/api/cache/redis/impl/RedisCacheImplicits.scala
index fccf66aa..7db3b3c6 100644
--- a/src/test/scala/play/api/cache/redis/impl/RedisCacheImplicits.scala
+++ b/src/test/scala/play/api/cache/redis/impl/RedisCacheImplicits.scala
@@ -46,7 +46,7 @@ object RedisCacheImplicits {
 
     protected def policy: RecoveryPolicy = new RecoverWithDefault {}
 
-    protected implicit val runtime = mock[RedisRuntime]
+    protected implicit val runtime: RedisRuntime = mock[RedisRuntime]
     runtime.context returns ExecutionContext.global
     runtime.invocation returns invocation
     runtime.prefix returns RedisEmptyPrefix
@@ -104,7 +104,7 @@ object RedisCacheImplicits {
     protected val classTagKey = s"classTag::$key"
     protected val classTagOther = s"classTag::$other"
 
-    protected implicit val environment = mock[Environment]
+    protected implicit val environment: Environment = mock[Environment]
     protected val async = mock[AsyncRedis]
     protected val cache: play.cache.redis.AsyncCacheApi = new AsyncJavaRedis(async)
 
diff --git a/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala
index 18bab084..2a6a0ac7 100644
--- a/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala
+++ b/src/test/scala/play/api/cache/redis/impl/RedisCacheSpec.scala
@@ -13,8 +13,6 @@ class RedisCacheSpec(implicit ee: ExecutionEnv) extends Specification with Reduc
 
   import org.mockito.ArgumentMatchers._
 
-  val key = "key"
-  val value = "value"
   val expiration = 1.second
 
   "Redis Cache" should {
@@ -213,7 +211,7 @@ class RedisCacheSpec(implicit ee: ExecutionEnv) extends Specification with Reduc
 
     "get or future (failing orElse)" in new MockedCache with OrElse {
       connector.get[String](anyString)(anyClassTag) returns None
-      cache.getOrFuture(key)(failedFuture) must throwA[TimeoutException].await
+      cache.getOrFuture[String](key)(failedFuture) must throwA[TimeoutException].await
       orElse mustEqual 2
     }
 
diff --git a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala
index 31891f64..96390493 100644
--- a/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala
+++ b/src/test/scala/play/api/cache/redis/impl/RedisRuntimeSpec.scala
@@ -8,7 +8,7 @@ class RedisRuntimeSpec extends Specification with WithApplication {
 
   import Implicits._
 
-  implicit val recoveryResolver = new RecoveryPolicyResolverImpl
+  implicit val recoveryResolver: RecoveryPolicyResolver = new RecoveryPolicyResolverImpl
 
   "RedisRuntime" should {
     import RedisRuntime._