How can I test a callback in isolation by manually calling it? - scala

Using scalatest, is it possible to somehow manual trigger a callback to test that my onComplete method gets called and doesn't throw an exception?
Since it returns Unit, I just want to make sure it gets called and doesn't error out.
class CustomOffsetCallback extends OffsetCommitCallback with Logging {
override def onComplete(offsets: util.Map[TopicPartition, OffsetAndMetadata], exception: Exception): Unit =
if (exception != null)
logger.error(s"...")
else
logger.debug(s"....")
}

Related

Future's .recover not getting called when Exception is thrown in Mockito unit test

The following code returns a Future.
val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys)
Then I process the Future
findUserFuture.flatMap {....}
.recover{...}
fineOne returns the Future and the Future wraps call to getOneById
def findOne(userKeys:UserKeys):Future[Option[User]] = {
Future{
//val loginInfo:LoginInfo = LoginInfo(userKeys.providerID,userKeys.authProvider)
val userOption:Option[User] = getOneById(userKeys)
userOption
}
}
I suppose that recover will be called if Future returned by findOne fails i.e. throws an Exception. So I am simulating that by making getOneById throw an exception.
when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user)))
when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) //simulating database error
But the unit test doesn't throw an exception and the test proceeds using value Future(Some(User)).
I also tried throwing the exception from findOne - when(mockUserRepository.findOne(userKeys)).thenThrow(classOf[RuntimeException]) but the test case stops
with the following two prints and the .recover of the Future is not called
java.lang.RuntimeException was thrown.
java.lang.RuntimeException
This findUserFuture: Future[Option[User]] or userRepo.findOne returns future,
hence you need to return Future.failed in your mock.
For ex.
when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user)))
when(mockUserRepository.getOneById(userKeys)).thenReturn(Future.failed(new RuntimeException("network failure"))
Find below complete working test to simulate your use case :
test("mock future test") {
case class User(name: String)
case class UserNotFoundException(name: String) extends Exception
trait UserRepo {
def findOne(name: String): Future[Option[User]]
}
val name = "bob"
val dummyUser = User("dummy")
val userRepo = mock[UserRepo]
when(userRepo.findOne(name)).thenReturn(Future.failed(new RuntimeException()))
val userF = userRepo
.findOne(name)
.flatMap {
case Some(user) ⇒ Future.successful(user)
case None ⇒ Future.failed(UserNotFoundException(name))
}
.recover {
case NonFatal(_) ⇒ dummyUser
}
userF.futureValue shouldBe dummyUser
}
Update *
After looking at the original post closely, I found small mistake in the way you are mocking.
try below:
when(mockUserRepository.findOne(userKeys)).thenCallRealMethod()
when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException])
Notice thenCallRealMethod() here, earlier you were mocking findOne to return future with successful value which means original method was not getting called which in turn was not calling getOneById
You can't mock the type you want to test, a mock has nothing of the original behaviour of that type.
If you wanna stub just some behaviour of the class under test (or any other type), you should use a spy, then you would only do
when(spyUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException])
then call spyUserRepository.findOne(userKeys) and assert that it returns a Failed future
That said, it seems you have the responsabilities a bit mixed up here, I'd suggest having a second look to your design as having to resort to use spys for this looks like a big code smell for me.

Verifying a method call with default parameter

Suppose, I have a method that takes a default parameter:
trait Client {
def foo(data: String, unneededStuff: String = null): String
}
class Foo(client: Client) {
def doStuff(data: String) = client.foo(data)
}
Making a unit test for Foo, I want to verify that it calls client exactly once, and doesn't do anything else with it:
class FooSpec extends FunSpec with MockitoSugar {
describe("Foo") {
it("calls client once") {
val client = mock[Client]
when(client.foo(any, any)).thenReturn("bar")
new Foo(client).doStuff("baz") shouldBe "bar"
Mockito.verify(client).foo("baz", null)
Mockito.verifyNoMoreInteractions(client)
}
}
}
This does not work, because the default parameter on Client.foo is implemented as another method call on Client, so verifyNoMoreInteractions fails.
I can work around this by passing null explicitly into the client.foo call in Foo, but that kinda defeats the point of having default parameters at all - I can never have them as long as I care about testing stuff :(
I was wondering if anyone has an idea of a better work around? I am willing to do tricks in the test code, but am avert to making the production code to weird things with the sole purpose of accommodating the tests.

Scala exceptions: missing catchingWithRethrow? Advice needed

In scala.util.control.Exception, there are many functions to create catchers and the likes. However, in some cases, I find that I would like to translate/rename/wrap an exception like this:
class MyBaseException...
class MyDerivedException extends MyBaseException ...
class MyOtherDerivedException extends MyBaseException ...
try {
// some code throw new MyDerivedException
// some other code throw new java.lang.IllegalArgumentException
} catch {
case e: MyBaseException => throw e // Let every exceptions derived from MyBaseException go through unchanged
case NonFatal(e) => new MyOtherDerivedException(e)
}
This can also be done with a catch like that:
try{ ... } catch {
case NonFatal(e) if (!classOf[MyBaseException].isAssignableFrom(e.getClass)) => new MyOtherDerivedException(e)
}
So now, fitting that into scala.util.control.Exception catching syntax, I didn't find any way of doing that. In practice, I want something like that:
def wouldMatch(x: Throwable, classes: scala.collection.Seq[Class[_]]): Boolean =
classes exists (_ isAssignableFrom x.getClass)
def shouldRethrowIfMatchOrElse(x: Throwable, classes: scala.collection.Seq[Class[_]]): Boolean = {
if (wouldMatch(x, classes)) true
else shouldRethrow(x)
}
def catchingWithRethrow[T](c: Catcher[T])(exceptions: Class[_]*): Catch[T] = new Catch(c, None, { shouldRethrowIfMatchOrElse(_, exceptions) })
And be used like that:
val myCatching = catchingWithRethrow(nonFatalCatcher)(classOf[MyBaseException]).withApply(e => new MyOtherDerivedException(e))
myCatching {
// some code throw new MyDerivedException
// some other code throw new java.lang.IllegalArgumentException
}
I find that Catch[T] should have a withRethrow(...) function to override the third parameters. This probably would be more elegant like that:
val myCatching = catching(nonFatalCatcher).withRethrow(...).withApply(e => new MyOtherDerivedException(e))
Did I miss something in scala.util.control.Exception that could accomplish that without custom code?
What are your thoughts on this?
I find that Catch[T] should have a withRethrow(...) function to override the third parameters. [...] Did I miss something in scala.util.control.Exception that could accomplish that without custom code?
Maybe there is something like a withRethrow in the standard library, but I haven't seen it either. Luckily, Scala allows us to enrich existing interfaces using implicit classes. Assuming you do not mind adding three lines of custom code, you could achieve the desired syntax:
implicit class WithRethrow[T](theCatch: Catch[T]) {
def withRethrow(exceptions: Class[_]*): Catch[T] = new Catch[T](theCatch.pf, theCatch.fin, t => exceptions exists (_.isAssignableFrom(t.getClass)))
}
With your use-case
// may need to import the implicit class if it is not already in scope
val myCatching = catching(nonFatalCatcher).withRethrow(classOf[MyBaseException]).withApply(e => throw new MyOtherDerivedException(e))
myCatching {
throw new OutOfMemoryError() // OutOfMemoryError is thrown because it is not non-fatal
throw new IllegalArgumentException // MyOtherDerivedException is thrown because IllegalArgumentException is not rethrown
throw new MyDerivedException // MyDerivedException is thrown because MyDerivedException is rethrown
}
It seems like a very specialized/unusual use case. It violates LSP. And using exceptions at all is unidiomatic in scala - scala.util.control.Exception is mainly about catching exceptions that library functions might throw and translating them into more idiomatic expressions of potential failures.
If you want to do this then write your own code for it - it should be pretty straightforward. I really don't think this is a common enough use case for there to be a standard library function for it.

ScalaTest: assert blocking statement

I'm using some code with blocking statement:
blocking {
Thread.sleep(10*1000)
}
Is there a way to assert that this blocking statement is given? Or in other words: Can I write a test that fails if somebody removes the blocking statement?
Update: How to assert blocking when used in Futures?
Try playing with BlockContext.
You should get something like this:
var blocked = false // flag to detect blocking
val oldContext = BlockContext.current
val myContext = new BlockContext {
override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = {
blocked = true
oldContext.blockOn(thunk)
}
}
BlockContext.withBlockContext(myContext) {
blocking {} // block (or not) here
}
assert(blocked) // verify that blocking happened
Update on making it work if you want to test code wrapped in Future (comment follow-up)
When you construct the Future it's factory method takes block of code (function) to execute explicitly and execution context implicitly (commonly scala.concurrent.ExecutionContext.Implicits.global).
The block of code later will be scheduled to execution context and will be runned in one of it's threads.
Now, if you simply wrap blocking piece of code into Future inside code block passed to BlockContext.withBlockContext, like you suggest in comment:
BlockContext.withBlockContext(myContext) {
Future {
blocking { Thread.sleep(100) }
}
}
... this will not work since your current thread will only do Future construction and actual code passed to Future will be executed in thread from relevant execution context (BlockContext.withBlockContext detects blockings in current thread).
Having that said, I can suggest you to do one of 3 things:
Do not wrap code you want to test into Future. If you want to test whether piece of code uses blocking or not - just do that.
Write a function and test it, you can pass it to Future in production.
Lets assume that for some reason you can't avoid creating Future in your test. In this case you'll have to tamper with execution context that is used when constucting future.
This code sample demonstrates how one could do that (reuse blocked and myContext from my original example):
// execution context that submits everything that is passed to it to global execution context
// it also wraps any work submited to it into block context that records blocks
implicit val ec = new ExecutionContext {
override def execute(runnable: Runnable): Unit = {
ExecutionContext.Implicits.global execute new Runnable {
override def run(): Unit = {
BlockContext.withBlockContext(myContext) {
runnable.run()
}
}
}
}
override def reportFailure(t: Throwable): Unit = {
ExecutionContext.Implicits.global.reportFailure(t)
}
}
// this future will use execution context defined above
val f = Future {
blocking {} // block (or not) here
}
Await.ready(f, scala.concurrent.duration.Duration.Inf)
assert(blocked)
If your Future gets created indirectly, for example, as a result calling some other function that you run in your test, then you'll have to somehow (possibly using dependency injection) drag your mocked execution context into wherever Future gets created and use it there to consruct it.
As you can see, the first option is the simpliest one and I suggest sticking to it if you can.

Failing a scalatest when akka actor throws exception outside of the test thread

I've had a situation come up and bite me a few times where I'm testing an Actor and the Actor throws an exception unexpectedly (due to a bug), but the test still passes. Now most of the time the exception in the Actor means that whatever the test is verifying won't come out properly so it the test fails, but in rare cases that's not true. The exception occurs in a different thread than the test runner so the test runner knows nothing about it.
One example is when I'm using a mock to verify some dependency gets called, and due to a mistake in the Actor code I call an unexpected method in the mock. That causes the mock to throw an exception which blows up the actor but not the test. Sometimes this can even cause downstream tests to fail mysteriously because of how the Actor blew up. For example:
// using scala 2.10, akka 2.1.1, scalatest 1.9.1, easymock 3.1
// (FunSpec and TestKit)
class SomeAPI {
def foo(x: String) = println(x)
def bar(y: String) = println(y)
}
class SomeActor(someApi: SomeAPI) extends Actor {
def receive = {
case x:String =>
someApi.foo(x)
someApi.bar(x)
}
}
describe("problem example") {
it("calls foo only when it receives a message") {
val mockAPI = mock[SomeAPI]
val ref = TestActorRef(new SomeActor(mockAPI))
expecting {
mockAPI.foo("Hi").once()
}
whenExecuting(mockAPI) {
ref.tell("Hi", testActor)
}
}
it("ok actor") {
val ref = TestActorRef(new Actor {
def receive = {
case "Hi" => sender ! "Hello"
}
})
ref.tell("Hi", testActor)
expectMsg("Hello")
}
}
"problemExample" passes, but then downstream "ok actor" fails for some reason I don't really understand... with this exception:
cannot reserve actor name '$$b': already terminated
java.lang.IllegalStateException: cannot reserve actor name '$$b': already terminated
at akka.actor.dungeon.ChildrenContainer$TerminatedChildrenContainer$.reserve(ChildrenContainer.scala:86)
at akka.actor.dungeon.Children$class.reserveChild(Children.scala:78)
at akka.actor.ActorCell.reserveChild(ActorCell.scala:306)
at akka.testkit.TestActorRef.<init>(TestActorRef.scala:29)
So, I can see ways of catching this sort of thing by examining the logger output in afterEach handlers. Definitely doable, although a little complicated in cases where I actually expect an exception and that's what I'm trying to test. But is there any more direct way of handling this and making the test fail?
Addendum: I have looked at the TestEventListener and suspect there's maybe something there that would help, but I can't see it. The only documentation I could find was about using it to check for expected exceptions, not unexpected ones.
Thinking in Actors there is also another solution: failures travel to the supervisor, so that is the perfect place to catch them and feed them into the test procedure:
val failures = TestProbe()
val props = ... // description for the actor under test
val failureParent = system.actorOf(Props(new Actor {
val child = context.actorOf(props, "child")
override val supervisorStrategy = OneForOneStrategy() {
case f => failures.ref ! f; Stop // or whichever directive is appropriate
}
def receive = {
case msg => child forward msg
}
}))
You can send to the actor under test by sending to failureParent and all failures—expected or not—go to the failures probe for inspection.
Other than examining the logs, I can think of two ways to fail tests when an actor crashes:
Ensure that no Terminated message is received
Check the TestActorRef.isTerminated property
The latter option is deprecated, so I'll ignore it.
Watching Other Actors from Probes describes how to setup a TestProbe. In this case it might look something like:
val probe = TestProbe()
probe watch ref
// Actual test goes here ...
probe.expectNoMessage()
If the actor dies due to an exception it will generate the Terminated message. If that happens during the test and you expect something else, the test will fail. If it happens after your last message expectation, then the expectNoMessage() should fail when Terminated is received.
Okay, I've had a little time to play with this. I've got a nice solution that uses an event listener and filter to catch errors. (Checking isTerminated or using TestProbes is probably good in more focused cases but seems awkward when trying to make something to mix into any old test.)
import akka.actor.{Props, Actor, ActorSystem}
import akka.event.Logging.Error
import akka.testkit._
import com.typesafe.config.Config
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.mock.EasyMockSugar
import scala.collection.mutable
trait AkkaErrorChecking extends ShouldMatchers {
val system:ActorSystem
val errors:mutable.MutableList[Error] = new mutable.MutableList[Error]
val errorCaptureFilter = EventFilter.custom {
case e: Error =>
errors += e
false // don't actually filter out this event - it's nice to see the full output in console.
}
lazy val testListener = system.actorOf(Props(new akka.testkit.TestEventListener {
addFilter(errorCaptureFilter)
}))
def withErrorChecking[T](block: => T) = {
try {
system.eventStream.subscribe(testListener, classOf[Error])
filterEvents(errorCaptureFilter)(block)(system)
withClue(errors.mkString("Akka error(s):\n", "\n", ""))(errors should be('empty))
} finally {
system.eventStream.unsubscribe(testListener)
errors.clear()
}
}
}
You can just use withErrorChecking inline at specific spots, or mix it into a Suite and use withFixture to do it globally across all tests, like this:
trait AkkaErrorCheckingSuite extends AkkaErrorChecking with FunSpec {
override protected def withFixture(test: NoArgTest) {
withErrorChecking(test())
}
}
If you use this in my original example, then you will get the first test "calls foo only when it receives a message" to fail, which is nice because that's where the real failure is. But the downstream test will still fail as well due to the system blowing up. To fix that, I went a step further and used a fixture.Suite to instance a separate TestKit for each test. That solves lots of other potential test isolation issues when you have noisy actors. It requires a little more ceremony declaring each test but I think it's well worth it. Using this trait with my original example I get the first test failing and the second one passing which is just what I want!
trait IsolatedTestKit extends ShouldMatchers { this: fixture.Suite =>
type FixtureParam = TestKit
// override this if you want to pass a Config to the actor system instead of using default reference configuration
val actorSystemConfig: Option[Config] = None
private val systemNameRegex = "[^a-zA-Z0-9]".r
override protected def withFixture(test: OneArgTest) {
val fixtureSystem = actorSystemConfig.map(config => ActorSystem(systemNameRegex.replaceAllIn(test.name, "-"), config))
.getOrElse (ActorSystem (systemNameRegex.replaceAllIn(test.name, "-")))
try {
val errorCheck = new AkkaErrorChecking {
val system = fixtureSystem
}
errorCheck.withErrorChecking {
test(new TestKit(fixtureSystem))
}
}
finally {
fixtureSystem.shutdown()
}
}
}