guilhebl
guilhebl

Reputation: 8990

how to mock external WS API calls in Scala Play framework

I have an existing Scala play application which has a REST API that calls another external REST API. I want to mock the external Web service returning fake JSON data for internal tests. Based on example from: https://www.playframework.com/documentation/2.6.x/ScalaTestingWebServiceClients

I followed example exactly as in Documentation and I'm getting compiler errors due to deprecated class Action.

import play.core.server.Server
import play.api.routing.sird._
import play.api.mvc._
import play.api.libs.json._
import play.api.test._

import scala.concurrent.Await
import scala.concurrent.duration._

import org.specs2.mutable.Specification
import product.services.market.common.GitHubClient

class GitHubClientSpec extends Specification {
  import scala.concurrent.ExecutionContext.Implicits.global

  "GitHubClient" should {
    "get all repositories" in {

      Server.withRouter() {
        case GET(p"/repositories") => Action {
          Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
        }
      } { implicit port =>
        WsTestClient.withClient { client =>
          val result = Await.result(
            new GitHubClient(client, "").repositories(), 10.seconds)
          result must_== Seq("octocat/Hello-World")
        }
      }
    }
  }
}

object Action in package mvc is deprecated: Inject an ActionBuilder (e.g. DefaultActionBuilder) or extend BaseController/AbstractController/InjectedController

And this is the primary example from latest official docs which in fact contains a compile time error, given this example doesn't work how should be the proper way to easily mock an external API using Scala Play?

Upvotes: 1

Views: 3623

Answers (2)

tartakynov
tartakynov

Reputation: 2858

If you're using standalone version of play-ws you can use this library https://github.com/f100ded/play-fake-ws-standalone like this

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import org.f100ded.play.fakews._
import org.scalatest._
import play.api.libs.ws.JsonBodyWritables._

import scala.concurrent.duration.Duration
import scala.concurrent._
import scala.language.reflectiveCalls

/**
  * Tests MyApi HTTP client implementation
  */
class MyApiClientSpec extends AsyncFlatSpec with BeforeAndAfterAll with Matchers {

  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()
  import system.dispatcher

  behavior of "MyApiClient"

  it should "put access token to Authorization header" in {
    val accessToken = "fake_access_token"
    val ws = StandaloneFakeWSClient {
      case request @ GET(url"http://host/v1/foo/$id") =>
        // this is here just to demonstrate how you can use URL extractor
        id shouldBe "1"

        // verify access token
        request.headers should contain ("Authorization" -> Seq(s"Bearer $accessToken"))

        Ok(FakeAnswers.foo)
    }

    val api = new MyApiClient(ws, baseUrl = "http://host/", accessToken = accessToken)
    api.getFoo(1).map(_ => succeed)
  }

  // ... more tests

  override def afterAll(): Unit = {
    Await.result(system.terminate(), Duration.Inf)
  }

}

Upvotes: 0

rethab
rethab

Reputation: 8403

You may change your example to:

Server.withRouterFromComponents() { cs => {
    case GET(p"/repositories") => cs.defaultActionBuilder {
      Results.Ok(Json.arr(Json.obj("full_name" -> "octocat/Hello-World")))
    }
  }
} { implicit port =>
  WsTestClient.withClient { client =>
    val result = Await.result(
      new GitHubClient(client, "").repositories(), 10.seconds)
    result should be(Seq("octocat/Hello-World"))
  }
}

To be honest, I'm not 100% sure if this is the nicest way. However I have submitted a PR to the play framework so you might watch that space for comments from the makers.

Upvotes: 3

Related Questions