# Testing

## Module info

To use Pekko Persistence TestKit, add the module to your project:

@@dependency[sbt,Maven,Gradle] {
  bomGroup=org.apache.pekko bomArtifact=pekko-bom_$scala.binary.version$ bomVersionSymbols=PekkoVersion
  symbol1=PekkoVersion
  value1="$pekko.version$"
  group1=org.apache.pekko
  artifact1=pekko-persistence-typed_$scala.binary.version$
  version1=PekkoVersion
  group2=org.apache.pekko
  artifact2=pekko-persistence-testkit_$scala.binary.version$
  version2=PekkoVersion
  scope2=test
}

@@project-info{ projectId="persistence-testkit" }

## Unit testing

**Note!** The `EventSourcedBehaviorTestKit` is a new feature, api may have changes breaking source compatibility in future versions.

Unit testing of `EventSourcedBehavior` can be done with the @apidoc[EventSourcedBehaviorTestKit]. It supports running
one command at a time and you can assert that the synchronously returned result is as expected. The result contains the
events emitted by the command and the new state after applying the events. It also has support for verifying the reply
to a command.

You need to configure the `ActorSystem` with the `EventSourcedBehaviorTestKit.config`. The configuration enables
the in-memory journal and snapshot storage.

Scala
:  @@snip [AccountExampleDocSpec.scala](/cluster-sharding-typed/src/test/scala/docs/org/apache/pekko/cluster/sharding/typed/AccountExampleDocSpec.scala) { #testkit }

Java
:  @@snip [AccountExampleDocTest.java](/cluster-sharding-typed/src/test/java/jdocs/org/apache/pekko/cluster/sharding/typed/AccountExampleDocTest.java) { #testkit } 

A full test for the `AccountEntity`, which is shown in the @ref:[Persistence Style Guide](persistence-style.md), may look like this:

Scala
:  @@snip [AccountExampleDocSpec.scala](/cluster-sharding-typed/src/test/scala/docs/org/apache/pekko/cluster/sharding/typed/AccountExampleDocSpec.scala) { #test }

Java
:  @@snip [AccountExampleDocTest.java](/cluster-sharding-typed/src/test/java/jdocs/org/apache/pekko/cluster/sharding/typed/AccountExampleDocTest.java) { #test }  

Serialization of commands, events and state are verified automatically. The serialization checks can be
customized with the `SerializationSettings` when creating the `EventSourcedBehaviorTestKit`. By default,
the serialization roundtrip is checked but the equality of the result of the serialization is not checked.
`equals` must be implemented @scala[(or using `case class`)] in the commands, events and state if `verifyEquality`
is enabled.

To test recovery the `restart` method of the `EventSourcedBehaviorTestKit` can be used. It will restart the
behavior, which will then recover from stored snapshot and events from previous commands. It's also possible
to populate the storage with events or simulate failures by using the underlying @apidoc[PersistenceTestKit].

## Persistence TestKit

**Note!** The `PersistenceTestKit` is a new feature, api may have changes breaking source compatibility in future versions.

Persistence testkit allows to check events saved in a storage, emulate storage operations and exceptions.
To use the testkit you need to add the following dependency in your project:

@@dependency[sbt,Maven,Gradle] {
  bomGroup=org.apache.pekko bomArtifact=pekko-bom_$scala.binary.version$ bomVersionSymbols=PekkoVersion
  symbol1=PekkoVersion
  value1="$pekko.version$"
  group="org.apache.pekko"
  artifact="pekko-persistence-testkit_$scala.binary.version$"
  version=PekkoVersion
}

There are two testkit classes which have similar api:

 * @apidoc[PersistenceTestKit] class is for events
 * @apidoc[SnapshotTestKit] class is for snapshots
 
The testkit classes have two corresponding plugins which emulate the behavior of the storages: 

 * @apidoc[PersistenceTestKitPlugin] class emulates a events storage 
 * @apidoc[PersistenceTestKitSnapshotPlugin] class emulates a snapshots storage

**Note!** The corresponding plugins **must** be configured in the actor system which is used to initialize the particular testkit class:

Scala
:  @@snip [Configuration.scala](/docs/src/test/scala/docs/persistence/testkit/Configuration.scala) { #testkit-typed-conf }

Java
:  @@snip [Configuration.java](/docs/src/test/java/jdocs/persistence/testkit/Configuration.java) { #testkit-typed-conf }

and

Scala
:  @@snip [Configuration.scala](/docs/src/test/scala/docs/persistence/testkit/Configuration.scala) { #snapshot-typed-conf }

Java
:  @@snip [Configuration.java](/docs/src/test/java/jdocs/persistence/testkit/Configuration.java) { #snapshot-typed-conf }

A typical scenario is to create a persistent actor, send commands to it and check that it persists events as it is expected:

Scala
:  @@snip [TestKitExamples.scala](/docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #test }

Java
:  @@snip [PersistenceTestKitSampleTest.java](/docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitSampleTest.java) { #test }

You can safely use persistence testkit in combination with main pekko testkit.

The main methods of the api allow to (see @apidoc[PersistenceTestKit] and @apidoc[SnapshotTestKit] for more details):

 * check if the given event/snapshot object is the next persisted in the storage.
 * read a sequence of persisted events/snapshots.
 * check that no events/snapshots have been persisted in the storage.
 * throw the default exception from the storage on attempt to persist, read or delete the following event/snapshot.
 * clear the events/snapshots persisted in the storage.
 * reject the events, but not snapshots (rejections are not supported for snapshots in the original api).
 * set your own [policy](#setting-your-own-policy-for-the-storage) which emulates the work of the storage. 
Policy determines what to do when persistence needs to execute some operation on the storage (i.e. read, delete, etc.).
 * get all the events/snapshots persisted in the storage
 * put the events/snapshots in the storage to test recovery
 
#### Setting your own policy for the storage

You can implement and set your own policy for the storage to control its actions on particular operations, for example you can fail or reject events on your own conditions.
Implement the @apidoc[ProcessingPolicy[EventStorage.JournalOperation]] @scala[trait]@java[interface] for event storage
or @apidoc[ProcessingPolicy[SnapshotStorage.SnapshotOperation]] @scala[trait]@java[interface] for snapshot storage,
and set it with `withPolicy()` method.

Scala
:  @@snip [TestKitExamples.scala](/docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #policy-test }

Java
:  @@snip [PersistenceTestKitPolicySampleTest.java](/docs/src/test/java/jdocs/persistence/testkit/PersistenceTestKitPolicySampleTest.java) { #policy-test }

`tryProcess()` method of the @apidoc[ProcessingPolicy] has two arguments: persistence id and the storage operation. 

Event storage has the following operations:

 * @apidoc[ReadEvents] Read the events from the storage.
 * @apidoc[WriteEvents] Write the events to the storage.
 * @apidoc[DeleteEvents] Delete the events from the storage.
 * @apidoc[ReadSeqNum$] Read the highest sequence number for particular persistence id.

Snapshot storage has the following operations:

 * @apidoc[ReadSnapshot] Read the snapshot from the storage.
 * @apidoc[WriteSnapshot] Write the snapshot to the storage.
 * @apidoc[DeleteSnapshotsByCriteria] Delete snapshots in the storage by criteria.
 * @apidoc[DeleteSnapshotByMeta] Delete particular snapshot from the storage by its metadata.

The `tryProcess()` method must return one of the processing results:
 
 * @apidoc[ProcessingSuccess] Successful completion of the operation. All the events will be saved/read/deleted.
 * @apidoc[StorageFailure] Emulates exception from the storage.
 * @apidoc[Reject] Emulates rejection from the storage.

**Note** that snapshot storage does not have rejections. If you return `Reject` in the `tryProcess()` of the snapshot storage policy, it will have the same effect as the `StorageFailure`.

Here is an example of the policy for an event storage:

Scala
:  @@snip [TestKitExamples.scala](/docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #set-event-storage-policy }

Java
:  @@snip [TestKitExamples.java](/docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java) { #set-event-storage-policy }

Here is an example of the policy for a snapshot storage:

Scala
:  @@snip [TestKitExamples.scala](/docs/src/test/scala/docs/persistence/testkit/TestKitExamples.scala) { #set-snapshot-storage-policy }

Java
:  @@snip [TestKitExamples.java](/docs/src/test/java/jdocs/persistence/testkit/TestKitExamples.java) { #set-snapshot-storage-policy } 

### Configuration of Persistence TestKit

There are several configuration properties for persistence testkit, please refer
to the @ref:[reference configuration](../general/configuration-reference.md#config-pekko-persistence-testkit)

## Integration testing

`EventSourcedBehavior` actors can be tested with the @ref:[ActorTestKit](testing-async.md) together with
other actors. The in-memory journal and snapshot storage from the @ref:[Persistence TestKit](#persistence-testkit)
can be used also for integration style testing of a single `ActorSystem`, for example when using Cluster Sharding
with a single Cluster node.

For tests that involve more than one Cluster node you have to use another journal and snapshot store.
While it's possible to use the @ref:[Persistence Plugin Proxy](../persistence-plugins.md#persistence-plugin-proxy)
it's often better and more realistic to use a real database.

### Plugin initialization

Some Persistence plugins create tables automatically, but has the limitation that it can't be done concurrently
from several ActorSystems. That can be a problem if the test creates a Cluster and all nodes tries to initialize
the plugins at the same time. To coordinate initialization you can use the `PersistenceInit` utility.

`PersistenceInit` is part of `pekko-persistence-testkit` and you need to add the dependency to your project:

@@dependency[sbt,Maven,Gradle] {
  bomGroup=org.apache.pekko bomArtifact=pekko-bom_$scala.binary.version$ bomVersionSymbols=PekkoVersion
  symbol1=PekkoVersion
  value1="$pekko.version$"
  group="org.apache.pekko"
  artifact="pekko-persistence-testkit_$scala.binary.version$"
  version=PekkoVersion
}

Scala
:  @@snip [PersistenceInitSpec.scala](/docs/src/test/scala/docs/persistence/testkit/PersistenceInitSpec.scala) { #imports #init }

Java
:  @@snip [PersistenceInitTest.java](/docs/src/test/java/jdocs/persistence/testkit/PersistenceInitTest.java) { #imports #init }
  
