Search code examples
unit-testingscalasprayspray-client

How to mock spray-client response


I have a simple spray client :

val pipeline = sendReceive ~> unmarshal[GoogleApiResult[Elevation]]

val responseFuture = pipeline {Get("http://maps.googleapis.com/maps/api/elevation/jsonlocations=27.988056,86.925278&sensor=false") }

responseFuture onComplete {
  case Success(GoogleApiResult(_, Elevation(_, elevation) :: _)) =>
    log.info("The elevation of Mt. Everest is: {} m", elevation)
    shutdown()

  case Failure(error) =>
    log.error(error, "Couldn't get elevation")
    shutdown()
}

Full code can be found here.

I want to mock the response of the server to test the logic in the Success and Failure cases. The only relevant information i found was here but I haven't been able to use the cake pattern to mock the sendReceive method.

Any suggestion or example would be greatly appreciated.


Solution

  • Here's an example of one way to mock it using specs2 for the test spec and mockito for the mocking. First, the Main object refactored into a class setup for mocking:

    class ElevationClient{
      // we need an ActorSystem to host our application in
      implicit val system = ActorSystem("simple-spray-client")
      import system.dispatcher // execution context for futures below
      val log = Logging(system, getClass)
    
      log.info("Requesting the elevation of Mt. Everest from Googles Elevation API...")
    
      import ElevationJsonProtocol._
      import SprayJsonSupport._
    
      def sendAndReceive = sendReceive
    
      def elavation = {
        val pipeline = sendAndReceive ~> unmarshal[GoogleApiResult[Elevation]]
    
        pipeline {
          Get("http://maps.googleapis.com/maps/api/elevation/json?locations=27.988056,86.925278&sensor=false")
        }   
      }
    
    
      def shutdown(): Unit = {
        IO(Http).ask(Http.CloseAll)(1.second).await
        system.shutdown()
      }
    }
    

    Then, the test spec:

    class ElevationClientSpec extends Specification with Mockito{
    
      val mockResponse = mock[HttpResponse]
      val mockStatus = mock[StatusCode]
      mockResponse.status returns mockStatus
      mockStatus.isSuccess returns true
    
      val json = """
        {
           "results" : [
              {
                 "elevation" : 8815.71582031250,
                 "location" : {
                    "lat" : 27.9880560,
                    "lng" : 86.92527800000001
                 },
                 "resolution" : 152.7032318115234
              }
           ],
           "status" : "OK"
        }    
        """
    
      val body = HttpEntity(ContentType.`application/json`, json.getBytes())
      mockResponse.entity returns body
    
      val client = new ElevationClient{
        override def sendAndReceive = {
          (req:HttpRequest) => Promise.successful(mockResponse).future
        }
      }
    
      "A request to get an elevation" should{
        "return an elevation result" in {
          val fut = client.elavation
          val el = Await.result(fut, Duration(2, TimeUnit.SECONDS))
          val expected = GoogleApiResult("OK",List(Elevation(Location(27.988056,86.925278),8815.7158203125)))
          el mustEqual expected
        }
      }
    }
    

    So my approach here was to first define an overridable function in the ElevationClient called sendAndReceive that just delegates to the spray sendReceive function. Then, in the test spec, I override that sendAndReceive function to return a function that returns a completed Future wrapping a mock HttpResponse. This is one approach for doing what you want to do. I hope this helps.