scalaakkaakka-testkit

TestProbe not receiving messages from RouteSpec


I'm having issues when trying to "mock" out an Actor behind a Route. I want to be able to override and simulate functionality at test time, and I think TestProbe is the correct way to approach this.

However, I have not gotten a TestProbe to receive a single request. When using probe.expectMsg(request), the test fails with assertion failed: timeout (3 seconds) during expectMsg while waiting for GetCardRequest(12345). Removing the expectMsg and reply calls will still cause the test to fail due to Request was rejected at the check block. I'm expecting val result = request ~> routes ~> runRoute to hit the underlying TestProbe.

I feel like I'm just not understanding something simple about the setup! Thanks for the help in advance!

class MyRoutesSpec 
  extends WordSpec 
  with Matchers 
  with ScalaFutures 
  with ScalatestRouteTest
  with MyRoutes {
  lazy val routes = MyRoutes

  val probe = new TestProbe(system)
  override val cardLookupActor = probe.ref

  //TODO figure out how to get TestProbe to actually work!!
  "My Routes" should {
    "be able to get a card from a request" in {
      val cardRequest = GetCardRequest("12345")
      val cardRequestEntity = Marshal(cardRequest).to[MessageEntity].futureValue // futureValue is from ScalaFutures
      val request = Post("/getcard").withEntity(cardRequestEntity)
      val cardResponse = ClientCard("Hello", "World")

      val result = request ~> routes ~> runRoute

      probe.expectMsg(cardRequest)
      probe.reply(cardResponse)

      check {
        status should ===(StatusCodes.Created)
        contentType should ===(ContentTypes.`application/json`)
        entityAs[String] should ===("""{"cardName":"Hello", "cardType":"World"}""")
      } (result)
    }
  }
}


trait MyRoutes extends JsonSupport {

  // we leave these abstract, since they will be provided by the App
  implicit def system: ActorSystem

  lazy val log = Logging(system, classOf[MyRoutes])

  // other dependencies that Routes use
  def cardLookupActor: ActorRef

  // Required by the `ask` (?) method below
  implicit lazy val timeout = Timeout(5.seconds) 

  lazy val myRoutes: Route =
    pathPrefix("getcard") {
      concat(
        path(Segment) { id =>
          concat(
            get {
              val cardFuture: Future[ClientCard] =
                (cardLookupActor ? GetCardRequest(id = id)).mapTo[ClientCard]
            })
        })
    }
}

Solution

  • Your tests works but it just proves that test probe is never called.

    Check this test that passes OK

    import akka.Done
    import akka.actor.ActorRef
    import akka.actor.ActorSystem
    import akka.event.Logging
    import akka.http.scaladsl.server._
    import akka.http.scaladsl.server.Directives._
    import akka.http.scaladsl.model.StatusCodes
    import akka.http.scaladsl.testkit.ScalatestRouteTest
    import akka.testkit.TestProbe
    import akka.util.Timeout
    import org.scalatest.Matchers
    import org.scalatest.WordSpec
    import org.scalatest.concurrent.ScalaFutures
    import akka.pattern.ask
    
    import concurrent.duration._
    
    class MyRoutesSpec extends WordSpec with Matchers with ScalaFutures with ScalatestRouteTest with MyRoutes {
    
      val probe = new TestProbe(system)
      override val cardLookupActor = probe.ref
    
      //TODO figure out how to get TestProbe to actually work!!
      "My Routes" should {
        "be able to get a card from a request" in {
          val cardRequest = "12345"
          val request = Get("/getcard/sss").withEntity(cardRequest)
          val cardResponse = "HelloWorld"
    
          val result = request ~> myRoutes ~> runRoute
    
          probe.expectMsg(Done)
          probe.reply(cardResponse)
    
          check {
            status should ===(StatusCodes.OK)
            entityAs[String] should ===("""HelloWorld""")
          }(result)
        }
      }
    }
    
    trait MyRoutes {
    
      // we leave these abstract, since they will be provided by the App
      implicit def system: ActorSystem
    
      lazy val log = Logging(system, classOf[MyRoutes])
    
      // other dependencies that Routes use
      def cardLookupActor: ActorRef
    
      // Required by the `ask` (?) method below
      implicit lazy val timeout = Timeout(5.seconds)
    
      lazy val myRoutes: Route =
        pathPrefix("getcard") {
          path(Segment) { _ =>
            get {
              complete((cardLookupActor ? Done).mapTo[String])
            }
          }
        }
    }
    

    What fixed