# Unit Testing with Scala 3 - STTP/Tapir/ZIO

**URL:** <https://softwaremill.community/t/unit-testing-with-scala-3-sttp-tapir-zio/213>\
**Category:** General\
**Created:** [May 30, 2023, 8:30pm UTC](https://softwaremill.community/t/unit-testing-with-scala-3-sttp-tapir-zio/213 "2023-05-30T20:30:35Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Paul\_Folbrecht](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/paul_folbrecht/32/148_2.png) [@Paul\_Folbrecht](https://softwaremill.community/u/Paul_Folbrecht)\
**Post date:** [May 30, 2023, 8:30pm UTC](https://softwaremill.community/t/unit-testing-with-scala-3-sttp-tapir-zio/213/1 "2023-05-30T20:30:35Z")

</div>

We’ve got a new project that’s Scala 3/ZIO 2-based and have discovered that idioms for unit testing that worked with Scala 2/ZIO 1 (Scala 2 is the main factor here) are not working. The endpoints work fine as a real server - it’s just testing that’s been a bugaboo.

Sparing details, here is some sample code provided graciously by Adam (before I mentioned this is Dotty-only) that does not compile. Below is the code and the compiler error. This is a Dotty issue, not an sttp/Tapir one, but if anyone has a workaround for this, much appreciated. I’ve been unable to find a compiler workaround even with brute force (casting, etc.).

Sample Code:

```auto
import sttp.tapir.EndpointIO.annotations.jsonbody
import sttp.tapir.ztapir._
import sttp.client3._
import sttp.client3.impl.zio.RIOMonadAsyncError
import sttp.client3.testing.SttpBackendStub
import sttp.tapir.{EndpointInput, Schema}
import sttp.tapir.server.stub.TapirStubInterpreter
import sttp.tapir.json.zio._
import zio.{Console, ZIO, ZIOAppDefault}
import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder}

object TestWithJsonBodyUsingZioJson extends ZIOAppDefault {
  case class Payload(one: String, two: String)
  implicit val encoder: JsonEncoder[Payload] = DeriveJsonEncoder.gen[Payload]
  implicit val decoder: JsonDecoder[Payload] = DeriveJsonDecoder.gen[Payload]
  implicit val schema: Schema[Payload] = Schema.derived[Payload]

  case class RequestInput(@jsonbody payload: Payload)

  val input = EndpointInput.derived[RequestInput]
  val myEndpoint = endpoint.post.in(input).out(stringBody).zServerLogic(r => ZIO.succeed(s"Got request: $r"))

  val stub = TapirStubInterpreter(SttpBackendStub(new RIOMonadAsyncError[Any]))
    .whenServerEndpoint(myEndpoint)
    .thenRunLogic()
    .backend()
  val body = """
              {
                "one": "",
                "two": ""
              }
            """.stripMargin
  val response = basicRequest
    .contentType("application/json")
    .body(body)
    .post(uri"http://test.com/test-request")
    .send(stub)

  override def run = response.flatMap { r =>
    Console.printLine(r.toString())
  }
}

```

Error:

```auto
[error] -- [E007] Type Mismatch Error: /Users/ehq178/dev/pfc-card-dynamic-mapper/server/src/test/scala/com/capitalone/prometheus/Temp.scala:24:24 

[error] 24 | .whenServerEndpoint(myEndpoint)

[error] | ^^^^^^^^^^

[error] |Found: (TestWithJsonBodyUsingZioJson.myEndpoint : Any)

[error] |Required: sttp.tapir.server.ServerEndpoint.Full[A, U, I, E, O, Nothing, 

[error] | [_] =>> zio.RIO[Any, _]

[error] |]

[error] |

[error] |where: A is a type variable with constraint 

[error] | E is a type variable with constraint 

[error] | I is a type variable with constraint 

[error] | O is a type variable with constraint 

[error] | U is a type variable with constraint 

```

---

<div class="post-metadata">

**Author:** ![Paul\_Folbrecht](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/paul_folbrecht/32/148_2.png) [@Paul\_Folbrecht](https://softwaremill.community/u/Paul_Folbrecht)\
**Post date:** [May 31, 2023, 3:03pm UTC](https://softwaremill.community/t/unit-testing-with-scala-3-sttp-tapir-zio/213/2 "2023-05-31T15:03:19Z")

</div>

Ok, typing explicitly to

_root_.sttp.tapir.ztapir.ZServerEndpoint[Any, Any]

makes the compiler happy.

---

<div class="post-metadata">

**Author:** ![adamw](https://dub1.discourse-cdn.com/flex005/user_avatar/softwaremill.community/adamw/32/28_2.png) [@adamw](https://softwaremill.community/u/adamw)\
**Post date:** [June 7, 2023, 7:00am UTC](https://softwaremill.community/t/unit-testing-with-scala-3-sttp-tapir-zio/213/3 "2023-06-07T07:00:48Z")

</div>

Indeed. The problem seems to be with typing the following:

```plaintext
val myEndpoint = endpoint.post.in(stringBody).out(stringBody)
  .zServerLogic(r => ZIO.succeed(s"Got request: $r"))

```

This gets typed as `ZServerEndpoint[Nothing, Any]`. That is, the `R` type parameter to `zServerLogic` gets inferred as `Nothing`. Adding an explicit type annotation, or the type parameter solves the problem:

```plaintext
val myEndpoint = endpoint.post.in(stringBody).out(stringBody)
  .zServerLogic[Any](r => ZIO.succeed(s"Got request: $r"))

```

It would seem that the `R` should be inferred as `Any`, as that’s what is dictated by the given `ZIO.succeed`, which is parameter passed here: `def zServerLogic[R](logic: INPUT => ZIO[R, ERROR_OUTPUT, OUTPUT])`. But because of `R`’s variance this get widened to `Nothing`.

Not sure if this is fixable, I’ll add a note in the documentation for now, to explicitly provide the type of the environment when using Scala 3.
