Testing ======= If you need a stub backend for use in tests instead of a "real" backend (you probably don't want to make HTTP calls during unit tests), you can use the ``SttpBackendStub`` class. It allows specifying how the backend should respond to requests matching given predicates. A backend stub can be created using an instance of a "real" backend, or by explicitly giving the response wrapper monad and supported streams type. For example:: implicit val testingBackend = SttpBackendStub(HttpURLConnectionBackend()) .whenRequestMatches(_.uri.path.startsWith(List("a", "b"))) .thenRespond("Hello there!") .whenRequestMatches(_.method == Method.POST) .thenRespondServerError() val response1 = sttp.get(uri"http://example.org/a/b/c").send() // response1.body will be Right("Hello there") val response2 = sttp.post(uri"http://example.org/d/e").send() // response2.code will be 500 It is also possible to match requests by partial function, returning a response. E.g.:: implicit val testingBackend = SttpBackendStub(HttpURLConnectionBackend()) .whenRequestMatchesPartial({ case r if r.uri.path.endsWith(List("partial10")) => Response(Right(10), 200, Nil, Nil) case r if r.uri.path.endsWith(List("partialAda")) => Response(Right("Ada"), 200, Nil, Nil) }) val response1 = sttp.get(uri"http://example.org/partial10").send() // response1.body will be Right(10) val response2 = sttp.post(uri"http://example.org/partialAda").send() // response2.body will be Right("Ada") This approach to testing has one caveat: the responses are not type-safe. That is, the backend cannot match on or verify that the type included in the response matches the response type requested. Simulating exceptions --------------------- If you want to simulate an exception being thrown by a backend, e.g. a socket timeout exception, you can do so by throwing the appropriate exception instead of the response, e.g.:: implicit val testingBackend = SttpBackendStub(HttpURLConnectionBackend()) .whenRequestMatches(_ => true) .thenRespond(throw new TimeoutException()) Adjusting the response body type -------------------------------- If the type of the response body returned by the stub's rules (as specified using the ``.whenXxx`` methods) doesn't match what was specified in the request, the stub will attempt to convert the body to the desired type. This might be useful when: * testing code which maps a basic response body to a custom type, e.g. mapping a raw json string using a decoder to a domain type * reading a classpath resource (which results in an ``InputStream``) and requesting a response of e.g. type ``String`` The following conversions are supported: * anything to ``()`` (unit), when the response is ignored * ``InputStream`` and ``Array[Byte]`` to ``String`` * ``InputStream`` and ``String`` to ``Array[Byte]`` * ``InputStream``, ``String`` and ``Array[Byte]`` to custom types through mapped response specifications For example:: implicit val testingBackend = SttpBackendStub(HttpURLConnectionBackend()) .whenRequestMatches(_ => true) .thenRespond(""" {"username": "john", "age": 65 } """) def parseUserJson(a: Array[Byte]): User = ... val response = sttp.get(uri"http://example.com") .response(asByteArray.map(parseUserJson)) .send() In the example above, the stub's rules specify that a response with a ``String``-body should be returned for any request; the request, on the other hand, specifies that responses should be parsed from a byte array to a custom ``User`` type. These type don't match, so the ``SttpBackendStub`` will in this case convert the body to the desired type. Note that no conversions will be attempted for streaming response bodies. Delegating to another backend ----------------------------- It is also possible to create a stub backend which delegates calls to another (possibly "real") backend if none of the specified predicates match a request. This can be useful during development, to partially stub a yet incomplete API with which we integrate:: implicit val testingBackend = SttpBackendStub.withFallback(HttpURLConnectionBackend()) .whenRequestMatches(_.uri.path.startsWith(List("a"))) .thenRespond("I'm a STUB!") val response1 = sttp.get(uri"http://api.internal/a").send() // response1.body will be Right("I'm a STUB") val response2 = sttp.post(uri"http://api.internal/b").send() // response2 will be whatever a "real" network call to api.internal/b returns