毎日Learning

学んだことを共有します

Akka Persistence Testkitを動かすまでの手順をメモっておく

Akka Version 2.6.5ぐらいから、Akka Persistence Testkitなるライブラリが出ていたそうで、ちょっと動かしてみたので、そのときにやってことをメモ。

doc.akka.io

そのときのコードは、以下です。

github.com

以下、手順とかコードとか書きます。

build.sbt

まずは、build.sbtの作り方から。

とはいえ、依存ライブラリを必要なものを最低限入れるだけ。

val akkaVersion = "2.6.6"

lazy val root = (project in file("."))
  .settings(
    name := "akka-sample-persistence",
    version := "0.1",
    scalaVersion := "2.13.3",
    libraryDependencies ++= Seq(
        "com.typesafe.akka" %% "akka-actor-typed"           % akkaVersion,
        "com.typesafe.akka" %% "akka-persistence-typed"     % akkaVersion,
        "com.typesafe.akka" %% "akka-slf4j"                 % akkaVersion,
        "com.typesafe.akka" %% "akka-serialization-jackson" % akkaVersion,
        "ch.qos.logback"     % "logback-classic"            % "1.2.3",
        "com.typesafe.akka" %% "akka-actor-testkit-typed"   % akkaVersion % Test,
        "com.typesafe.akka" %% "akka-persistence-testkit"   % akkaVersion % Test,
        "org.scalatest"     %% "scalatest"                  % "3.2.0"     % Test
      )
  )

必要なライブラリのポイントとしては、 Serializationlogback あたりかなーと。

Serialization

AkkaのDocumentに書いてあるサンプルのコードを動かそうと思うと、

doc.akka.io

ここに書いてあるサンプルのActorでテストをしようとしている。

その中で、 CborSerializable がActorのプロトコルにextendsされている。

これは、Akka Persistenceで永続化するときに、どうやってSerializeしますかーってあたりの解決をしてあげる必要がある。

このあたりのSerializationの解決は、以下のドキュメントに書いてあるあたりを参考にする。

doc.akka.io

というのも、今回のドキュメントで、 EventSourcedBehaviorTestKit なるTestKitを試そうとしているのだが、その初期設定値でSerializationはちゃんと指定しろよ的なことになっているようなので。

logback

サンプルのテストコードの中で LogCapturing を指定しているので、logback-test.xmlを作ってって対応をする必要がある。

doc.akka.io

このドキュメントで紹介されている LogCapturing を使いたいみたいなので、logbackの依存がいるし、ここに記載されている logback-test.xmltest/resources ディレクトリの配下に作ってあげないとだめ。

サンプルのActor

Akkaのドキュメントに書いてあるAccountの前にもちょっと簡単なActorを作った。

これもドキュメントに書いてるコードだけど、一部、自分でちょちょっとイジってるけど。

object MyPersistentBehavior {
  sealed trait Command extends CborSerializable
  final case class Add(data: String) extends Command
  case object Clear                  extends Command

  sealed trait Event extends CborSerializable
  final case class Added(data: String) extends Event
  case object Cleared                  extends Event

  final case class State(history: List[String]) extends CborSerializable

  private val commandHandler: (State, Command) => Effect[Event, State] =
    (state, command) =>
      command match {
        case Add(data) => Effect.persist(Added(data))
        case Clear     => Effect.persist(Cleared)
      }

  private val eventHandler: (State, Event) => State =
    (state, event) =>
      event match {
        case Added(data) => state.copy((data :: state.history).take(5))
        case Cleared     => State(Nil)
      }

  def apply(id: String): Behavior[Command] =
    EventSourcedBehavior[Command, Event, State](
      persistenceId = PersistenceId.ofUniqueId(id),
      emptyState = State(Nil),
      commandHandler,
      eventHandler
    )

}

コード自体は、Akka Persistenceの EventSourcedBehavior をapplyする典型的な例になっている。

ポイントは、 CborSerializable なるtraitを自分で作っている。

trait CborSerializable {}

こんな感じで。

これを、TestクラスのConfigあたりで、良い感じにSerializeするクラスはこれですよー的な感じで指定してやると、簡単にSerializeされるようになる。

サンプルのTest

class MyPersistentBehaviorSpec
    extends ScalaTestWithActorTestKit(
      ConfigFactory
        .parseString("""
         |akka {
         |  actor {
         |    serializers {
         |      jackson-cbor = "akka.serialization.jackson.JacksonCborSerializer"
         |    }
         |    serialization-bindings {
         |      "com.github.yoshiyoshifujii.akka.sample.persistence.serialization.CborSerializable" = jackson-cbor
         |    }
         |  }
         |}
         |""".stripMargin).withFallback(EventSourcedBehaviorTestKit.config)
    )
    with AnyWordSpecLike
    with BeforeAndAfterEach
    with LogCapturing {

  private val eventSourcedTestKit =
    EventSourcedBehaviorTestKit[MyPersistentBehavior.Command, MyPersistentBehavior.Event, MyPersistentBehavior.State](
      system,
      MyPersistentBehavior("id-1")
    )

  override protected def beforeEach(): Unit = {
    super.beforeEach()
    eventSourcedTestKit.clear()
  }

  "MyPersistentBehavior" must {

    "Add" in {
      val result = eventSourcedTestKit.runCommand(MyPersistentBehavior.Add("data-1"))
      result.stateOfType[MyPersistentBehavior.State].history should contain("data-1")

    }

  }
}

こんな感じ。

Akkaのドキュメント見てると、 extends ScalaTestWithActorTestKit(EventSourcedBehaviorTestKit.config) みたいな感じでしれっといけそうに書いてあるのだが、デフォでSerializationを指定しないといけなくなっているので、ConfigFactoryでそのあたりを指定してやる必要がある。

ここで、作っておいた CborSerializable を良い感じに指定してあげているので、ちゃんとテストが動くはず。

まとめ

これで、Akka Persistenceのテストいろいろと試せるぜー

なお、ドキュメントでは、このTestKitは新しくできたばっかりで将来変わる可能性あるよってあるので、この記事は期間限定で使える感じになりそう。

ので、詳しくは、Akkaのドキュメント読んでね。

以上です。