be smart when testing your akka code

62
Blood, sweat and tears ... or be smart when testing your Akka code

Upload: mykhailo-kotsur

Post on 16-Apr-2017

315 views

Category:

Engineering


1 download

TRANSCRIPT

Page 1: Be smart when testing your Akka code

Blood, sweat and tears

... or be smart when testing your Akka code

Page 2: Be smart when testing your Akka code

• 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

github:mkotsur/restito

Page 4: Be smart when testing your Akka code

• 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
Page 6: Be smart when testing your Akka code

• Tests;

• Better tests;

• Problem-less tests.

Page 7: Be smart when testing your Akka code

• Concurrency, parallelism, state.

• Not just messages;

• Willing to help you with the TestKit.

Page 8: Be smart when testing your Akka code
Page 9: Be smart when testing your Akka code

Not only messages

Page 10: Be smart when testing your Akka code

Not only messages

Page 11: Be smart when testing your Akka code

Not only messages

Page 12: Be smart when testing your Akka code

Not only messages

Page 13: Be smart when testing your Akka code

Not only messages

Page 14: Be smart when testing your Akka code

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

Sync unit-testing

• Works with `CallingThreadDispatcher`;

• Supports either message-sending style, or direct invocations.

Page 16: Be smart when testing your Akka code

class IncrementorActorTest extends TestKit(ActorSystem(“test-system")) {

...

}

Page 17: Be smart when testing your Akka code

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

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

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
Page 21: Be smart when testing your Akka code

Not only messages

Page 22: Be smart when testing your Akka code

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

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

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

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

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

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

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

Ignores

def ignoreMsg(pf: PartialFunction[AnyRef, Boolean])

def ignoreNoMsg()

Page 30: Be smart when testing your Akka code

Death watching

val probe = TestProbe()

probe watch target target ! PoisonPill

probe.expectTerminated(target)

Page 31: Be smart when testing your Akka code

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

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

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

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

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

• Tests;

• Better tests;

• Problem-less tests.

Page 37: Be smart when testing your Akka code

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

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

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

Timeouts

akka.test.single-expect-default = 3 seconds

akka.test.timefactor = 10

Page 41: Be smart when testing your Akka code

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

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

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

Dynamic actors

case class Identify(messageId: Any)

case class ActorIdentity( correlationId: Any, ref: Option[ActorRef] )

Page 45: Be smart when testing your Akka code
Page 46: Be smart when testing your Akka code
Page 47: Be smart when testing your Akka code

• Continuous delivery;

• No dedicated QA engineers;

• 500+ Jenkins jobs.

We depend on tests

Page 48: Be smart when testing your Akka code

• Tests;

• Better tests;

• Problem-less tests.

Page 49: Be smart when testing your Akka code

Be careful with mocking.

Page 50: Be smart when testing your Akka code

Prefer checking messages over checking side-effects.

Page 51: Be smart when testing your Akka code

Single responsibility principle.

Page 52: Be smart when testing your Akka code

Run your tests on slow VM and different OS.

Page 53: Be smart when testing your Akka code

Extract *all* timeouts into conf files. So that you can easily override them on Jenkins.

Page 54: Be smart when testing your Akka code

Don’t hesitate to rewrite test.

Page 55: Be smart when testing your Akka code

Don't hesitate to rewrite application code.

Page 56: Be smart when testing your Akka code

Don’t trust assertion errors, check logs.

Page 57: Be smart when testing your Akka code

Base your decisions on historical data.

Page 58: Be smart when testing your Akka code
Page 59: Be smart when testing your Akka code
Page 60: Be smart when testing your Akka code

Be humane and spread the word.

Page 61: Be smart when testing your Akka code
Page 62: Be smart when testing your Akka code

Questions?

github:mkotsur/akka-smart-testing