Core Java

Java Based Akka application Part 2: Adding tests

On the previous blog we focused on spinning up our first Akka project.
Now it’s time to add a test for our codebase.

First thing to get started is adding the right dependencies to the existing project.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<dependencies>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor-typed_2.13</artifactId>
            <version>${akka.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
            <scope>provided</scope>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor-testkit-typed_2.13</artifactId>
            <version>${akka.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

What you shall notice is the usage of Junit 4, instead of Junit 5. Some of the testing utils like TestKitJunitResource need annotations like @ClassRule and are bound to Junit4. Obviously this is not a blocker on using JUnit 5, with some tweaks it is feasible to use the tools your project needs. However in this example Junit 4 shall be used.

Before we write the test we need to think of our code.
It is obvious that we sent a message to our actor in a fire and forget fashion.

1
2
3
4
private Behavior<GuardianMessage> receiveMessage(MessageToGuardian messageToGuardian) {
        getContext().getLog().info("Message received: {}",messageToGuardian.getMessage());
        return this;
    }

If you don’t have a way to intercept what happens inside the method your options are limited. In those cases you can utilise the log messages and actually expect log events to happen.

Before we add the unit test we need to make some logback adjustments. This will take effect only on our test logback.xml. More specific we need to have an appender on logback that captured the data. This is the CapturingAppender.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>[%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg MDC: {%mdc}%n</pattern>
        </encoder>
    </appender>
    <!-- Logging from tests are silenced by this appender. When there is a test failure the captured logging events are flushed to the appenders defined for the akka.actor.testkit.typed.internal.CapturingAppenderDelegate logger. -->
    <appender name="CapturingAppender" class="akka.actor.testkit.typed.internal.CapturingAppender" />
    <!-- The appenders defined for this CapturingAppenderDelegate logger are used when there is a test failure and all logging events from the test are flushed to these appenders. -->
    <logger name="akka.actor.testkit.typed.internal.CapturingAppenderDelegate" >
      <appender-ref ref="STDOUT"/>
    </logger>
    <root level="DEBUG">
        <appender-ref ref="CapturingAppender"/>
    </root>
</configuration>

Now it’s time to add the unit test.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.gkatzioura;
import akka.actor.testkit.typed.javadsl.LogCapturing;
import akka.actor.testkit.typed.javadsl.LoggingTestKit;
import akka.actor.testkit.typed.javadsl.TestKitJunitResource;
import akka.actor.testkit.typed.javadsl.TestProbe;
import akka.actor.typed.ActorRef;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
public class AppGuardianTests {
    @ClassRule
    public static final TestKitJunitResource testKit = new TestKitJunitResource();
    @Rule
    public final LogCapturing logCapturing = new LogCapturing();
    @Test
    public void testReceiveMessage() {
        ActorRef<AppGuardian.GuardianMessage> underTest = testKit.spawn(AppGuardian.create(), "app-guardian");
        LoggingTestKit.info("Message received: hello")
                .expect(
                        testKit.system(),
                        () -> {
                            underTest.tell(new AppGuardian.MessageToGuardian("hello"));
                            return null;
                        });
    }
}

Once we run the test the expected outcome is to pass. The actor did receive the message, did execute a logging action and this was captured by the CapturingAppender. Then the logging event was validated if it was the expected one. In case of exception probably you need to check if the logback.xml took effect.

As always you can find the source code on github.

Published on Java Code Geeks with permission by Emmanouil Gkatziouras, partner at our JCG program. See the original article here: Java Based Akka application Part 2: Adding tests

Opinions expressed by Java Code Geeks contributors are their own.

Emmanouil Gkatziouras

He is a versatile software engineer with experience in a wide variety of applications/services.He is enthusiastic about new projects, embracing new technologies, and getting to know people in the field of software.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button