I am writing unit tests for some async sections of my code (returning Future
s) that also involves the need to mock a Scala object.
Following these docs, I can successfully mock the object's functions. My question stems from the fact that withObjectMocked[FooObject.type]
returns Unit
, where async tests in scalatest require either an Assertion
or Future[Assertion]
to be returned. To get around this, I'm creating var
s in my tests that I reassign within the function sent to withObjectMocked[FooObject.type]
, which ends up looking something like this:
class SomeTest extends AsyncWordSpec with Matchers with AsyncMockitoSugar with ResetMocksAfterEachAsyncTest {
"wish i didn't need a temp var" in {
var ret: Future[Assertion] = Future.failed(new Exception("this should be something")) // <-- note the need to create the temp var
withObjectMocked[SomeObject.type] {
when(SomeObject.someFunction(any)) thenReturn Left(Error("not found"))
val mockDependency = mock[SomeDependency]
val testClass = ClassBeingTested(mockDependency)
ret = testClass.giveMeAFuture("test_id") map { r =>
r should equal(Error("not found"))
} // <-- set the real Future[Assertion] value here
}
ret // <-- finally, explicitly return the Future
}
}
My question then is, is there a better/cleaner/more idiomatic way to write async tests that mock objects without the need to jump through this bit of a hoop? For some reason, I figured using AsyncMockitoSugar
instead of MockitoSugar
would have solved that for me, but withObjectMocked
still returns Unit
. Is this maybe a bug and/or a candidate for a feature request (the async version of withObjectMocked
returning the value of the function block rather than Unit
)? Or am I missing how to accomplish this sort of task?
You should refrain from using
mockObject
in a multi-thread environment as it doesn't play well with it. This is because theobject
code is stored as a singleton instance, so it's effectively global.When you use
mockObject
you're efectibly forcefully overriding thisvar
(the code takes care of restoring the original, hence the syntax of usign it as a "resource" if you want).Because this
var
is global/shared, if you have multi-threaded tests you'll endup with random behaviour, this is the main reason why no async API is provided.In any case, this is a last resort tool, every time you find yourself using it you should stop and ask yourself if there isn't anything wrong with your code first, there are quite a few patterns to help you out here (like injecting the dependency), so you should rarely have to do this.