Search code examples
scalacats-effect

How to mock MVar2 make method


I'm having trouble mocking the MVar2 method make in my Unit test (using mockito).

I tried this:

private val sq = mock[MVar2[IO, String]]

and when I tried to mock the method like:

when(sq.take).thenReturn(
      "testString".pure[IO],
      IO.never
    )

I get the infinite loop in the test.

I was positive I could mock it like this.

The actual code calling the make is:

  def run: F[Unit] =
    fs2.Stream
      .repeatEval[F, String](signalQ.take)
      .map(encode)
      .unNone
      .evalTap(logMessage)
      .evalMap(send)
      .compile
      .drain

Solution

  • I see two issues.

    The first thing, there is no reason to mock IO. You can pretty much do this:

    // define this wherever you create sq 
    val sq = MVar2[IO, String]
    // then DI sq and
    sq.put("testString") >> run
    

    Then the second thing, how does .repeatEval(io) works? It calls io repeatedly, every time the stream needs another element. So at first it would evaluate io with your mock to:

    "testString".pure[IO]
    

    which would compute the first element to "testString" and later to

    IO.never
    

    which means that the second element would never appear as this io would never return - neither value nor exception. And your stream halts as you can see. (Which would be according to the spec but apparently not what you want).

    If you wanted to get Some(string) if value is present and None if it's absent (to terminate stream on None) you should try:

    def run: F[Unit] =
      fs2.Stream
        .repeatEval[F, Option[String]](signalQ.tryTake)// <-- try to get None on empty
        .unNoneTerminate // <-- terminate on None
        .map(encode)
        .evalTap(logMessage)
        .evalMap(send)
        .compile
        .drain