Search code examples
scalaplayframeworkscalatest

getting scheme java.lang.NullPointerException: scheme while trying to fake a server call in my service


I have a service class in my project and I want to test one of its methods that is performing an api call, so I want to catch this call and return something fake so I can test my method, it looks like this:

class MyService @Inject()(implicit config: Configuration, wsClient: WSClient) {

  def methodToTest(list: List[String]): Future[Either[BadRequestResponse, Unit]] = {
    wsClient.url(url).withHeaders(("Content-Type", "application/json")).post(write(list)).map { response =>
      response.status match {
        case Status.OK =>
          Right(logger.debug("Everything is OK!"))
        case Status.BAD_REQUEST =>
          Left(parse(response.body).extract[BadRequestResponse])
        case _ =>
          val ex = new RuntimeException(s"Failed with status: ${response.status} body: ${response.body}")
          logger.error(s"Service failed: ", ex)
          throw ex
      }
    }
  }
}

and now in my test class I go:

class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {

  implicit lazy val materializer: Materializer = app.materializer
  lazy val config: Configuration = app.injector.instanceOf[Configuration]
  lazy val myService = app.injector.instanceOf[MyService]

  "My Service Tests" - {

    "Should behave as im expecting" in {
      Server.withRouter() {
        case POST(p"/fake/api/in/conf") => Action { request =>
          Results.Ok
        }
      } { implicit port =>
        WsTestClient.withClient { implicit client =>

          whenReady(myService.methodToTest(List("1","2","3"))) { res =>
            res.isRight shouldBe true
          }
        }
      }
    }
  }
}

and I get this error:

scheme java.lang.NullPointerException: scheme

also tried put under client => :

val myService = new MyService {
         implicit val config: Configuration = configuration
         implicit val ws: WSClient = client
      }

but got some other error that I dont have enough arguments in the constructor...

why is it not working?

if there is a better nd simpler way to fake this api call i will love to hear it :)

thanks!


Solution

  • Server.withRouter may not be exactly what you want. It creates a server and bound it to a random port, per instance (unless you specify the port). It also creates its own instance of application which will be disconnected from the app you used to instantiate the service.

    Another thing is that the injected WSClient do not works relative to your application. You need to use the client which is passed to WsTestClient.withClient block instead. So, you should do something like:

    class MyServiceTest extends FreeSpec with ShouldMatchers with OneAppPerSuite with ScalaFutures with WsScalaTestClient {
    
      implicit lazy val materializer: Materializer = app.materializer
      lazy val config: Configuration = app.injector.instanceOf[Configuration]
    
      "My Service Tests" - {
    
        "Should behave as im expecting" in {
          Server.withRouter() {
            case POST(p"/fake/api/in/conf") => Action { request =>
              Results.Ok
            }
          } { implicit port =>
            WsTestClient.withClient { implicit client =>
    
              // Use the client "instrumented" by Play. It will
              // handle the relative aspect of the url.
              val myService = new MyService(client, config)
    
              whenReady(myService.methodToTest(List("1","2","3"))) { res =>
                res.isRight shouldBe true
              }
            }
          }
        }
      }
    }