Scalatestplus scalacheck for testing effectful api requires unsafeRunSync()

230 views Asked by At

I am exploring options to implement tests for effectful api. For brevity, I have the following api.

object util {
  import cats.effect.Sync

  def stringReverser(string: String): String            = string.reverse
  def stringReverserF[F[_]: Sync](s: String): F[String] = Sync[F].delay(s.reverse)

}

I use AsyncWordSpec with ScalaCheckPropertyChecks. I had to use unsafeRunSync.


class SampleAsyncWordSpec extends wordspec.AnyWordSpec  
      with org.scalatestplus.scalacheck.ScalaCheckPropertyChecks {

  import util._
  import cats.effect.unsafe.implicits.global
  "String reverser[F] with property based tests" must {
    "satisfy consistency with length in original and reversed string" in {
      forAll {(s: String) => 
        val ioAssertion  = stringReverserF[IO](s).map(r => assert(s.length() == r.length() + 1))  
        ioAssertion.unsafeRunSync()
      }
    }
  }

}

Is there a way to avoid calling unsafeRunSync() and leave it to the framework. In scalatestplus with scalacheck, there is no mention of testing effectful api.

PS: This is a trimmed down example of my goal. I am exploring on options to write rules for tagless final algebra.

2

There are 2 answers

1
Daenyth On BEST ANSWER

As others have answered, when using scalatest with scalacheck, it's not possible to avoid this.

However, there's nothing stopping you from having multiple test frameworks in your codebase if a full migration isn't possible.

I recommend using scalacheck-effect with the munit integration. I've successfully mixed scalacheck+munit in the same codebase before for similar reasons - sbt test will run them all. It's possible that you could write a scalatest integration for scalacheck-effect similar to the munit one, but I wouldn't bother honestly.

I typically also recommend using munit over scalatest for other reasons; if it's an option to migrate your tests, you should consider it. It's not as hard as you might think, depending what scalatest DSL you're using

1
Gastón Schabas On

ScalaTest by itself, is not design for testing effects.

If you check the official docs of cats-effects testing, there is a list with the different libs that you can use. You have cats-effect-testing which can be integrated with ScalaTest and scalacheck. You have scalacheck-effect too that adds support for writing effectful properties with the shape A => F[Assertion].

Also if you check cats-effect - testing # best practices it says

Avoid using unsafe* methods in tests, the same as you'd avoid them in "main" code. Writing tests to be structured the same way as "normal" code results in tests that are less likely to be flaky, act as executable documentation, and remain easy to read.

Use a compatible framework's support for writing IO[Assertion] style tests.

So, what you are doing in the test you wrote as an example, it's not exactly good