'UnsupportedOperationException: no provider' when testing a route that asks a reply from a typed actor

Hi. I’m running into the following problem.
I don’t know if I’m missing something, or I’m hitting a bug.
I have a typed actor-behaviour like this:

case class GreetMe(message: String, replyTo: ActorRef[Greet])
case class Greet(response: String)
val greetBehaviour: Behavior[GreetMe] = Behaviors.receive {
    case (ctx, GreetMe(m, to)) => to ! Greet(m)
      Behaviors.same
  }

Then I have a htttp-route that should, on an incoming request, create a response which contains a ‘Greet’ (coming from the actor).
The route uses the ask-pattern to ask a ‘Greet’ from an actorRef[GreetMe].
This is the route:

  implicit val scheduler: Scheduler
  implicit val timeout: Timeout
  implicit val ec: ExecutionContext
  val greetActor: ActorRef[GreetMe]
val route = onComplete(getGreet){
      case scala.util.Success(msg) => complete(msg)
      case scala.util.Failure(exception) => complete(exception.getMessage)
    }
  def getGreet: Future[String] = {
   val fut: Future[Greet] = greetActor ? (replyTo => GreetMe("hello", replyTo))
    fut.map(resp => {
      resp.response
    })
  }

All of this works fine and as expected.
But the problem is when I create a test for this route.
The test checks whether the actorRef[GreetMe] received the expected ‘GreetMe’ message.
The actual receiving of the ‘Greet’-reply is not checked because this test doesn’t have such an actor, only an inbox. The correctness of the Behaviour[GreetMe] is tested in another test.

class RoutesSpec extends WordSpec with Matchers with ScalatestRouteTest with Routes {
  val inbox: TestInbox[GreetMe] = TestInbox[GreetMe]()
  override val greetActor: ActorRef[GreetMe] = inbox.ref
  implicit override val ec = system.dispatcher
  implicit val scheduler: Scheduler = system.scheduler
  implicit val timeout: Timeout = Timeout(3.seconds)
  "the greeter" should {
    "return a greet" in {
      val request = HttpRequest(uri = "/hi")
      request ~> route ~> check {
        val receivedMesssage = inbox.receiveMessage()
        receivedMesssage.isInstanceOf[GreetMe] shouldBe true
      }
    }
  }
}

It fails with this exception, which is thrown on the line of the ask:

09:48:58.754 [th-RoutesSpec-akka.actor.default-dispatcher-2] ERROR akka.actor.ActorSystemImpl - Error during processing of request: 'no provider'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
java.lang.UnsupportedOperationException: no provider
	at akka.actor.testkit.typed.internal.FunctionRef.provider(StubbedActorContext.scala:48)
	at akka.actor.typed.scaladsl.AskPattern$PromiseRef.<init>(AskPattern.scala:127)
	at akka.actor.typed.scaladsl.AskPattern$.akka$actor$typed$scaladsl$AskPattern$$askUntyped(AskPattern.scala:138)
	at akka.actor.typed.scaladsl.AskPattern$Askable$.ask$extension(AskPattern.scala:99)
	at akka.actor.typed.scaladsl.AskPattern$Askable$.$qmark$extension(AskPattern.scala:63)
	at th.Routes.getGreet(RouteTest.scala:34)
	at th.Routes.getGreet$(RouteTest.scala:33)
	at th.RoutesSpec.getGreet(RouteTest.scala:40)
	at th.Routes.$anonfun$route$1(RouteTest.scala:29)
	at akka.http.scaladsl.server.directives.FutureDirectives.$anonfun$onComplete$2(FutureDirectives.scala:37)
	at akka.http.scaladsl.server.directives.ExecutionDirectives.$anonfun$handleExceptions$2(ExecutionDirectives.scala:32)
	at akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:161)
	at akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:149)
	at akka.http.scaladsl.testkit.RouteTest$WithTransformation2.$tilde$greater(RouteTest.scala:124)
	at th.RoutesSpec.$anonfun$new$2(RouteTest.scala:49)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)

It refers to this line: https://github.com/akka/akka/blob/v2.5.23/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/internal/StubbedActorContext.scala#L48

Knowing that it actually works in the application, I’m thinking that I’m missing something in the setup of the test. But there are no compile warnings/errors.
Thanks for any input!

No one?
Should I file an issue report for this? It’s easy enough to reproduce.

Thanks for the report @thomashoutekier, that error message is indeed not helpful. What it wants to say is that you cannot use testing ActorRefs created for synchronous testing with the ask pattern.

In the latest version about testing we clarified that, in general, asynchronous testing is recommended over synchronous testing. For your test it means that you should use val probe = testKit.createTestProbe[Pong]() to create the probe actor and use probe.expectMessage to expect results.

I created https://github.com/akka/akka/pull/27414 to improve the error message.

Johannes

Thank you very much. That was it indeed!