From 5620043c1fc9fc846fa4a5fe91b2af7fb1ff6008 Mon Sep 17 00:00:00 2001 From: adamw Date: Mon, 9 Oct 2017 17:03:44 +0200 Subject: Backend stubs with fallback (thx to @gabro) --- .../sttp/testing/SttpBackendStub.scala | 39 ++++++++++++++++------ .../sttp/testing/SttpBackendStubTests.scala | 21 +++++++++++- 2 files changed, 49 insertions(+), 11 deletions(-) (limited to 'core') diff --git a/core/src/main/scala/com/softwaremill/sttp/testing/SttpBackendStub.scala b/core/src/main/scala/com/softwaremill/sttp/testing/SttpBackendStub.scala index 5724264..aa04ef3 100644 --- a/core/src/main/scala/com/softwaremill/sttp/testing/SttpBackendStub.scala +++ b/core/src/main/scala/com/softwaremill/sttp/testing/SttpBackendStub.scala @@ -19,7 +19,8 @@ import scala.language.higherKinds * a response is specified with the incorrect body type. */ class SttpBackendStub[R[_], S] private (rm: MonadError[R], - matchers: Vector[Matcher[_]]) + matchers: Vector[Matcher[_]], + fallback: Option[SttpBackend[R, S]]) extends SttpBackend[R, S] { /** @@ -31,15 +32,22 @@ class SttpBackendStub[R[_], S] private (rm: MonadError[R], new WhenRequest(p) override def send[T](request: Request[T, S]): R[Response[T]] = { - val response = matchers + matchers .collectFirst { case matcher if matcher(request) => matcher.response - } - .getOrElse(DefaultResponse) - - rm.unit(response.asInstanceOf[Response[T]]) + } match { + case Some(response) => wrapResponse(response) + case None => + fallback match { + case None => wrapResponse(DefaultResponse) + case Some(fb) => fb.send(request) + } + } } + private def wrapResponse[T](r: Response[_]): R[Response[T]] = + rm.unit(r.asInstanceOf[Response[T]]) + override def close(): Unit = {} override def responseMonad: MonadError[R] = rm @@ -57,7 +65,7 @@ class SttpBackendStub[R[_], S] private (rm: MonadError[R], def thenRespond[T](body: T): SttpBackendStub[R, S] = thenRespond(Response[T](Right(body), 200, Nil, Nil)) def thenRespond[T](resp: Response[T]): SttpBackendStub[R, S] = - new SttpBackendStub(rm, matchers :+ Matcher(p, resp)) + new SttpBackendStub(rm, matchers :+ Matcher(p, resp), fallback) } } @@ -71,16 +79,27 @@ object SttpBackendStub { * [[https://stackoverflow.com/questions/46642623/cannot-infer-contravariant-nothing-type-parameter]]. */ def apply[R[_], S, S2 <: S](c: SttpBackend[R, S]): SttpBackendStub[R, S2] = - new SttpBackendStub[R, S2](c.responseMonad, Vector.empty) + new SttpBackendStub[R, S2](c.responseMonad, Vector.empty, None) /** * Create a stub backend using the given response monad (which determines * how requests are wrapped), and any stream type. */ def apply[R[_], S](responseMonad: MonadError[R]): SttpBackendStub[R, S] = - new SttpBackendStub[R, S](responseMonad, Vector.empty) + new SttpBackendStub[R, S](responseMonad, Vector.empty, None) + + /** + * Create a stub backend which delegates send requests to the given fallback + * backend, if the request doesn't match any of the specified predicates. + */ + def withFallback[R[_], S, S2 <: S]( + fallback: SttpBackend[R, S]): SttpBackendStub[R, S2] = + new SttpBackendStub[R, S2](fallback.responseMonad, + Vector.empty, + Some(fallback)) - private val DefaultResponse = Response[Nothing](Left(""), 404, Nil, Nil) + private val DefaultResponse = + Response[Nothing](Left("Not Found"), 404, Nil, Nil) private case class Matcher[T](p: Request[T, _] => Boolean, response: Response[T]) { diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/SttpBackendStubTests.scala b/core/src/test/scala/com/softwaremill/sttp/testing/SttpBackendStubTests.scala index 538dd35..b80ae76 100644 --- a/core/src/test/scala/com/softwaremill/sttp/testing/SttpBackendStubTests.scala +++ b/core/src/test/scala/com/softwaremill/sttp/testing/SttpBackendStubTests.scala @@ -13,7 +13,7 @@ class SttpBackendStubTests extends FlatSpec with Matchers with ScalaFutures { .whenRequestMatches(_.method == Method.GET) .thenRespondServerError() - it should "use the first rule if it matches" in { + "backend stub" should "use the first rule if it matches" in { implicit val b = testingStub val r = sttp.get(uri"http://example.org/a/b/c").send() r.is200 should be(true) @@ -48,4 +48,23 @@ class SttpBackendStubTests extends FlatSpec with Matchers with ScalaFutures { val r = sttp.post(uri"http://example.org").send() r.futureValue.code should be(404) } + + val testingStubWithFallback = SttpBackendStub + .withFallback(testingStub) + .whenRequestMatches(_.uri.path.startsWith(List("c"))) + .thenRespond("ok") + + "backend stub with fallback" should "use the stub when response for a request is defined" in { + implicit val b = testingStubWithFallback + + val r = sttp.post(uri"http://example.org/c").send() + r.body should be(Right("ok")) + } + + it should "delegate to the fallback for unhandled requests" in { + implicit val b = testingStubWithFallback + + val r = sttp.post(uri"http://example.org/a/b").send() + r.is200 should be(true) + } } -- cgit v1.2.3