3

我正在尝试为 MockWS 和 Caffeine 的使用编写一个测试。不知何故,从 MockWS 返回的值无法被驱逐。当 executorContext 不同时我遇到过这个问题,但是即使我尝试通过使用 materializer 的 executorContext 来克服这个问题,它仍然无法驱逐 ws 的返回值。你对此有什么想法吗?

插件:

addSbtPlugin("com.typesafe.play" % "sbt-plugin"    % "2.8.7")

依赖项:

"com.github.blemale"       %% "scaffeine"    % "5.1.0",
"de.leanovate.play-mockws" %% "play-mockws"  % "2.8.1" % Test

FakeTicker.scala

import com.github.benmanes.caffeine.cache.Ticker

import java.util.concurrent.atomic.AtomicLong
import scala.concurrent.duration.Duration

class FakeTicker extends Ticker {
  private val nanos                  = new AtomicLong()
  private val autoIncrementStepNanos = new AtomicLong()

  override def read(): Long =
    nanos.getAndAdd(autoIncrementStepNanos.get())

  def advance(duration: Duration): FakeTicker = {
    advance(duration.toNanos)
    this
  }

  def advance(nanoseconds: Long): FakeTicker = {
    nanos.addAndGet(nanoseconds)
    this
  }

  def setAutoIncrement(duration: Duration): Unit = {
    this.autoIncrementStepNanos.set(duration.toNanos)
  }
}

(更新:25/08/2021)测试用例:

import com.github.blemale.scaffeine.{ AsyncLoadingCache, Scaffeine }
import mockws.{ MockWS, Route }
import org.scalatest.freespec.AnyFreeSpec
import play.api.mvc.Results.Ok
import play.api.test.Helpers.await

import java.util.concurrent.TimeUnit
import java.util.stream.Collectors.toList
import scala.compat.java8.DurationConverters.DurationOps
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.{ Random, Try }
import scala.jdk.CollectionConverters._

class CacheSpec extends AnyFreeSpec {
  import mockws.MockWSHelpers._

  implicit val ec = materializer.executionContext

  val fakeTicker = new FakeTicker

  case class Expired(ttl: Long, data: String)

  val cache: AsyncLoadingCache[String, Expired] = Scaffeine()
    .executor(ec)
    .ticker(fakeTicker)
    .refreshAfterWrite(1.hour)
    .expireAfter(
      create = (_: String, response: Expired) => response.ttl.milliseconds,
      update = (_: String, response: Expired, _: FiniteDuration) => response.ttl.milliseconds,
      read = (_: String, _: Expired, duration: FiniteDuration) => duration
    )
    .buildAsyncFuture[String, Expired](load(_))

  val ws: MockWS = MockWS {
    Route {
      case ("POST", "/test") =>
        Action {
          Ok(Random.nextString(10))
        }
    }
  }

  def load(s: String): Future[Expired] = {
    //Does not work:
    ws.url("/test").post("test").map { result => Expired(600000, result.body) }
    //Works:
    //Future(Expired(600000, Random.nextString(10)))
  }

  "get" - {
    "should pass" in {
      val first = await(cache.get("test"), 1, TimeUnit.MINUTES)
      println(first)
      fakeTicker.advance(2.hours)
      println(
        Try(
          "Expires after: " +
            cache.underlying
              .synchronous()
              .policy
              .expireVariably()
              .stream()
              .map(_.getExpiresAfter("test"))
              .map(_.get().toScala)
              .collect(toList())
              .asScala
        ).getOrElse("<no element exists>")
      )
      val second = await(cache.get("test"), 1, TimeUnit.MINUTES)
      assert(first != second)
    }
  }
}

输出(fakeTicker 不影响 ws 的结果驱逐):

...
Expired(600000,疕䚢篱ⳁ먚婄燂慽˥ᆯ)
Expires after: Buffer(600 seconds)
[info] - should pass *** FAILED ***
[info]   Expired(600000,疕䚢篱ⳁ먚婄燂慽˥ᆯ) equaled Expired(600000,疕䚢篱ⳁ먚婄燂慽˥ᆯ) (CacheSpec.scala:73)
...

示例代码仓库:https ://github.com/veysiertekin/mockws-caffeine-test

4

0 回答 0