alex
alex

Reputation: 2213

Scala: How to Unit Test a function I have which makes an API call using mock/stub?

I have a function which makes an API call to a an external API

Let us say the function does something simple like the below. FYI need to import scala.io.Source

  def myFunction(apiRequestUrl: String) : MyObject = {
    val response: String = Source.fromURL(apiRequestUrl).mkString
    val formatedResponse: MyObject = formatResponseFunction(response)
    formatedResponse
  }

I know that some error codes I could receive are 400, 404, etc... and I'd like to just handle any error code generically from this. How could this be done? The examples I've found seem to be testing a person's own built REST API, and not function calls to someone else's external API

Upvotes: 1

Views: 3130

Answers (2)

ultrasecr.eth
ultrasecr.eth

Reputation: 1497

First of all, you have to inject the dependencies you want to replace with mocks, static calls within a function can't be mocked/stubbed.

In your Scenario you have another problem: your dependency Source is an object, and you can't mock objects, only non-final classes and traits. Furthermore, it is considered a bad practice to mock 3rd party APIs.

A good way to solve all those problems would be to re-write the code like

trait HttpAdapter {
  def fromUrl(apiRequestUrl: String): String = Source.fromURL(apiRequestUrl).mkString
}

object HttpAdapter extends HttpAdapter

def myFunction(apiRequestUrl: String, httpAdapter: HttpAdapter = HttpAdapter) : MyObject = {
  val response: String = httpAdapter.fromUrl(apiRequestUrl)
  val formatedResponse: MyObject = formatResponseFunction(response)
  formatedResponse
}

"myFunction" should "work" in {
  //create mock
  val http = mock[HttpAdapter]
  
  //stub mock
  http.fromUrl("some url") shouldReturn "result"
  
  //inject mock
  myFunction("some url", http) shouldBe MyObject
}

Notice I've wrapped the third party API in a class I have full control over (HttpAdapter) and then I mock that one

Then I inject httpAdapter as a parameter, but I provide a default value so the callers do not need to worry about it, while I can still override it with a mock or stub in test code.

Also note that I've used mockito-scala instead of regular mockito, so the stubbing syntax is different.

Upvotes: 1

Chaitanya
Chaitanya

Reputation: 3638

For mocking the external service calls, you could use Mockito which is a mocking framework. Mockito is very simple to use and you can provide stubs for your external calls. For example

val m = mock[io.Source.type]

Here you mock the Source and then you provide your desired behaviour on invocation of the fromUrl function. i.e

when(m.fromUrl("external service url")) thenReturn("result")

Upvotes: 2

Related Questions