Download - Be smart when testing your Akka code
![Page 1: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/1.jpg)
Blood, sweat and tears
... or be smart when testing your Akka code
![Page 2: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/2.jpg)
• PHP, NodeJS, AngularJS, Python, Java, Scala;
• Living in the Netherlands, working at
• Developing release automation product: XL Release.
About me
![Page 3: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/3.jpg)
github:mkotsur/restito
![Page 4: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/4.jpg)
• TDD is great when done properly!
• Reactive complexity;
• Learned a lot during last 4 months.
Why testing?
![Page 5: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/5.jpg)
![Page 6: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/6.jpg)
• Tests;
• Better tests;
• Problem-less tests.
![Page 7: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/7.jpg)
• Concurrency, parallelism, state.
• Not just messages;
• Willing to help you with the TestKit.
![Page 8: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/8.jpg)
![Page 9: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/9.jpg)
Not only messages
![Page 10: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/10.jpg)
Not only messages
![Page 11: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/11.jpg)
Not only messages
![Page 12: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/12.jpg)
Not only messages
![Page 13: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/13.jpg)
Not only messages
![Page 14: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/14.jpg)
object IncrementorActorMessages {
case class Inc(i: Int)
}
class IncrementorActor extends Actor {
var sum: Int = 0
override def receive: Receive = { case Inc(i) => sum = sum + i }
}
![Page 15: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/15.jpg)
Sync unit-testing
• Works with `CallingThreadDispatcher`;
• Supports either message-sending style, or direct invocations.
![Page 16: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/16.jpg)
class IncrementorActorTest extends TestKit(ActorSystem(“test-system")) {
...
}
![Page 17: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/17.jpg)
it("should have sum = 0 by default") { val actorRef = TestActorRef[IncrementorActor]
actorRef.underlyingActor.sum shouldEqual 0 }
![Page 18: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/18.jpg)
it("should increment on new messages") { val actorRef = TestActorRef[IncrementorActor]
actorRef ! Inc(2) actorRef.underlyingActor.sum shouldEqual 2
actorRef.underlyingActor.receive(Inc(3)) actorRef.underlyingActor.sum shouldEqual 5 }
![Page 19: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/19.jpg)
class LazyIncrementorActor extends Actor {
var sum: Int = 0
override def receive: Receive = { case Inc(i) => Future { Thread.sleep(100) sum = sum + i } }
}
Not good enough
![Page 20: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/20.jpg)
![Page 21: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/21.jpg)
Not only messages
![Page 22: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/22.jpg)
object IncrementorActorMessages {
case class Inc(i: Int)
case object Result
}
class IncrementorActor extends Actor {
var sum: Int = 0
override def receive: Receive = {
case Inc(i) => sum = sum + i
case Result => sender() ! sum }
}
New message
![Page 23: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/23.jpg)
it("should have sum = 0 by default") { val actorRef = system .actorOf(Props(classOf[IncrementorActor]))
val probe = TestProbe() actorRef.tell(Result, probe.ref)
probe.expectMsg(0) }
Using TestProbe
![Page 24: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/24.jpg)
it("should have sum = 0 by default") { val actorRef = system .actorOf(Props(classOf[IncrementorActor]))
actorRef ! Result
expectMsg(0) }
Using TestProbe
... with ImplicitSender
![Page 25: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/25.jpg)
it("should increment on new messages") { val actorRef = system .actorOf(Props(classOf[IncrementorActor]))
actorRef ! Inc(2) actorRef ! Result
expectMsg(2)
actorRef ! Inc(3) actorRef ! Result
expectMsg(5) }
Using TestProbe
![Page 26: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/26.jpg)
expectMsg*
def expectMsg[T](d: Duration, msg: T): T
def expectMsgPF[T](d: Duration) (pf: PartialFunction[Any, T]): T
def expectMsgClass[T](d: Duration, c: Class[T]): T
def expectNoMsg(d: Duration) // blocks
![Page 27: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/27.jpg)
Fishing
def receiveN(n: Int, d: Duration): Seq[AnyRef]
def receiveWhile[T](max: Duration, idle: Duration, n: Int) (pf: PartialFunction[Any, T]): Seq[T]
def fishForMessage(max: Duration, hint: String) (pf: PartialFunction[Any, Boolean]): Any
![Page 28: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/28.jpg)
Awaits
def awaitCond(p: => Boolean, max: Duration, interval: Duration)
def awaitAssert(a: => Any, max: Duration, interval: Duration)
// from ScalaTest
def eventually[T](fun: => T) (implicit config: PatienceConfig): T
![Page 29: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/29.jpg)
Ignores
def ignoreMsg(pf: PartialFunction[AnyRef, Boolean])
def ignoreNoMsg()
![Page 30: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/30.jpg)
Death watching
val probe = TestProbe()
probe watch target target ! PoisonPill
probe.expectTerminated(target)
![Page 31: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/31.jpg)
Test probes as dependencies
class HappyParentActor(childMaker: ActorRefFactory => ActorRef) extends Actor {
val child: ActorRef = childMaker(context)
override def receive: Receive = { case msg => child.forward(msg) }
}
![Page 32: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/32.jpg)
Event filter
class MyActor extends Actor with ActorLogging {
override def receive: Receive = {
case DoSideEffect => log.info("Hello World!") } }
![Page 33: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/33.jpg)
Event filter
EventFilter.info( message = "Hello World!", occurrences = 1 ).intercept { myActor ! DoSomething }
akka.loggers = ["akka.testkit.TestEventListener"]
![Page 34: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/34.jpg)
Supervision
class MyActor extends Actor with ActorLogging {
override def supervisorStrategy: Unit = OneForOneStrategy() { case _: FatalException => SupervisorStrategy.Escalate case _: ShitHappensException => SupervisorStrategy.Restart }
}
![Page 35: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/35.jpg)
Supervision
val actorRef = TestActorRef[MyActor](MyActor.props()) val pf = actorRef.underlyingActor .supervisorStrategy.decider
pf(new FatalException()) should be (Escalate) pf(new ShitHappensException()) should be (Restart)
![Page 36: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/36.jpg)
• Tests;
• Better tests;
• Problem-less tests.
![Page 37: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/37.jpg)
TestBase
class MyActorTest extends TestKit(ActorSystem("test-system")) with FunSpecLike {
override protected def afterAll(): Unit = { super.afterAll() system.shutdown() system.awaitTermination() } }
![Page 38: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/38.jpg)
TestBaseclass MyActorTest extends TestKit(ActorSystem("my-system")) with AkkaTestBase { ... }
trait AkkaTestBase extends BeforeAndAfterAll with FunSpecLike { this: TestKit with Suite =>
override protected def afterAll() { super.afterAll() system.shutdown() system.awaitTermination() } }
![Page 39: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/39.jpg)
TestBase: v2class MyActorTest extends AkkaTestBase { ... }
abstract class AkkaTestBase extends TestKit(ActorSystem("test-system")) with FunSpecLike with BeforeAndAfterAll {
override protected def afterAll() { super.afterAll() system.shutdown() } }
![Page 40: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/40.jpg)
Timeouts
akka.test.single-expect-default = 3 seconds
akka.test.timefactor = 10
![Page 41: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/41.jpg)
Settings extensionclass Settings(...) extends Extension {
object Jdbc { val Driver = config.getString("app.jdbc.driver") val Url = config.getString("app.jdbc.url") }
}
![Page 42: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/42.jpg)
Settings extension
class MyActor extends Actor { val settings = Settings(context.system) val connection = client.connect( settings.Jdbc.Driver, settings.Jdbc.Url ) }
![Page 43: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/43.jpg)
Settings extension
val config = ConfigFactory.parseString(""" app.jdbc.driver = "org.h2.Driver" app.jdbc.url = "jdbc:h2:mem:repository" """)
val system = ActorSystem("testsystem", config)
![Page 44: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/44.jpg)
Dynamic actors
case class Identify(messageId: Any)
case class ActorIdentity( correlationId: Any, ref: Option[ActorRef] )
![Page 45: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/45.jpg)
![Page 46: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/46.jpg)
![Page 47: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/47.jpg)
• Continuous delivery;
• No dedicated QA engineers;
• 500+ Jenkins jobs.
We depend on tests
![Page 48: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/48.jpg)
• Tests;
• Better tests;
• Problem-less tests.
![Page 49: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/49.jpg)
Be careful with mocking.
![Page 50: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/50.jpg)
Prefer checking messages over checking side-effects.
![Page 51: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/51.jpg)
Single responsibility principle.
![Page 52: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/52.jpg)
Run your tests on slow VM and different OS.
![Page 53: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/53.jpg)
Extract *all* timeouts into conf files. So that you can easily override them on Jenkins.
![Page 54: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/54.jpg)
Don’t hesitate to rewrite test.
![Page 55: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/55.jpg)
Don't hesitate to rewrite application code.
![Page 56: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/56.jpg)
Don’t trust assertion errors, check logs.
![Page 57: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/57.jpg)
Base your decisions on historical data.
![Page 58: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/58.jpg)
![Page 59: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/59.jpg)
![Page 60: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/60.jpg)
Be humane and spread the word.
![Page 61: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/61.jpg)
![Page 62: Be smart when testing your Akka code](https://reader035.vdocuments.us/reader035/viewer/2022062904/58729fa01a28ab07208b5773/html5/thumbnails/62.jpg)
Questions?
github:mkotsur/akka-smart-testing