I have published a minimal project showcasing my problem at https://github.com/Zwackelmann/mockito-actor-test
In my project I refactored a couple of components from classes to objects in all cases where the class did not really have a meaningful state. Since some of these objects establish connections to external services that need to be mocked, I was happy to see that mockito-scala introduced the withObjectMocked context function, which allows mocking objects within the scope of the function.
This feature worked perfectly for me until I introduced Actors in the mix, which would ignore the mocked functions despite being in the withObjectMocked context.
For an extended explanation what I did check out my github example project from above which is ready to be executed via sbt run.
My goal is to mock the doit function below. It should not be called during tests, so for this demonstration it simply throws a RuntimeException.
object FooService {
  def doit(): String = {
    // I don't want this to be executed in my tests
    throw new RuntimeException(f"executed real impl!!!")
  }
}
The FooService.doit function is only called from the FooActor.handleDoit function. This function is called by the FooActor after receiving the Doit message or when invoked directly.
object FooActor {
  val outcome: Promise[Try[String]] = Promise[Try[String]]()
  case object Doit
  def apply(): Behavior[Doit.type] = Behaviors.receiveMessage { _ =>
    handleDoit()
    Behaviors.same
  }
  // moved out actual doit behavior so I can compare calling it directly with calling it from the actor
  def handleDoit(): Unit = {
    try {
      // invoke `FooService.doit()` if mock works correctly it should return the "mock result"
      // otherwise the `RuntimeException` from the real implementation will be thrown
      val res = FooService.doit()
      outcome.success(Success(res))
    } catch {
      case ex: RuntimeException =>
        outcome.success(Failure(ex))
    }
  }
}
To mock Foo.doit I used withObjectMocked as follows. All following code is within this block. To ensure that the block is not left due to asynchronous execution, I Await the result of the FooActor.outcome Promise.
withObjectMocked[FooService.type] {
  // mock `FooService.doit()`: The real method throws a `RuntimeException` and should never be called during tests
  FooService.doit() returns {
    "mock result"
  }
  // [...]
}
I now have two test setups: The first simply calls FooActor.handleDoit directly
def simpleSetup(): Try[String] = {
  FooActor.handleDoit()
  val result: Try[String] = Await.result(FooActor.outcome.future, 1.seconds)
  result
}
The second setup triggers FooActor.handleDoit via the Actor
def actorSetup(): Try[String] = {
  val system: ActorSystem[FooActor.Doit.type] = ActorSystem(FooActor(), "FooSystem")
  // trigger actor  to call `handleDoit`
  system ! FooActor.Doit
  // wait for `outcome` future. The 'real' `FooService.doit` impl results in a `Failure`
  val result: Try[String] = Await.result(FooActor.outcome.future, 1.seconds)
  system.terminate()
  result
}
Both setups wait for the outcome promise to finish before exiting the block.
By switching between simpleSetup and actorSetup I can test both behaviors. Since both are executed within the withObjectMocked context, I would expect that both trigger the mocked function. However actorSetup ignores the mocked function and calls the real method.
val result: Try[String] = simpleSetup()
// val result: Try[String] = actorSetup()
result match {
  case Success(res) => println(f"finished with result: $res")
  case Failure(ex) => println(f"failed with exception: ${ex.getMessage}")
}
// simpleSetup prints: finished with result: mock result
// actorSetup prints: failed with exception: executed real impl!!!
Any suggestions?