https://kotlinlang.org logo
#mockk
Title
# mockk
h

huehnerlady

07/23/2020, 7:10 AM
Testbleeding with @TestFactory: Hi, I am trying to minimize duplications in tests, so I want to Use JUnit5's TestFactory. It works fine for everything, unless it concerns mokk mocks. Example
Copy code
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory

class Test {

  private val testService: TestService = mockk(relaxed = true)


  private val testStrings = listOf("1", "2")

  class TestService {
    fun test(string: String): String {
      return string
    }
  }

  @TestFactory
  fun tests() = testStrings
      .map { input: String ->
        DynamicTest.dynamicTest("should call service once for '$input'") {
          every {
            testService.test(any())
          } returns "foo"

          testService.test(input)

          verify(exactly = 1) { testService.test(any()) }
        }
      }
}
As you can see in the test, I am calling the mock just once. still, the testoutput is as shown on the screenshot. The second tests always fails. What am I doing wrong here? Can anybody help me? 🙂 I am using mokk 1.10.0, kotlin 1.3.72 and junit 5.6.2
☑️ 1
t

thanksforallthefish

07/23/2020, 7:46 AM
if I am not mistaken, test factories creates dynamic tests, which means junit only starts a context once. this means your mock is not reset, thus keeping previous counters. I don’t remember anymore if
@AfterEach
can help you
Copy code
fun @AfterEach() cleanUp { clearAllMocks() }
otherwise you can just add
clearAllMocks()
after your verify EDIT: forgot fun name 🙂
h

huehnerlady

07/23/2020, 8:05 AM
@thanksforallthefish many thanks, I did not know that. This is really annoying, as I just want to prevent test duplication 😞 I though I found some nice workaround in junit5 for the behaviour of spock’s
where
. But I guess I have to look further
c

christophsturm

07/23/2020, 10:07 AM
you can try something like #minutest or #kotest
t

thanksforallthefish

07/23/2020, 10:57 AM
I am currently using kotest btw and I find it great. however, this particular would still be there. kotest otoh offers several testing style, you might find one that approximate spock’s where (though I just know spock by name, not sure what where even is)
c

christophsturm

07/23/2020, 11:07 AM
with minutest and kotest you can just create your tests in a loop like in the test factory, and you get before each and after each for free
h

huehnerlady

07/23/2020, 11:07 AM
super cool, I will try kotest for that now, thanks guys 🙂
@christophsturm is this the way you would do that? At least this is what I will try out now 🙂 seems to do the trick (with FreeSpec) so far
c

christophsturm

07/23/2020, 11:11 AM
I’m a minutest user myself and would probably do it with a loop. with kotest you can probably also do a data driven test, which looks even better. https://github.com/kotest/kotest/blob/master/doc/data_driven_testing.md
s

sam

07/25/2020, 5:07 PM
Here is your example converted to Kotest @huehnerlady You can just loop around like @christophsturm mentioned. Create the tests and fixtures inside the loop. I've used FunSpec as that's what I prefer, but you can use FreeSpec or anything else.
Copy code
class Test : FunSpec() {

   init {
      val testStrings = listOf("1", "2")
      testStrings.forEach { input ->
         test("should call service once for '$input'") {
            
            val testService: TestService = mockk(relaxed = true)
            
            every {
               testService.test(any())
            } returns "foo"
            
            testService.test(input)
            
            verify(exactly = 1) { testService.test(any()) }
         }
      }
   }

   class TestService {
      fun test(string: String): String {
         return string
      }
   }
}
And if your TestService has multiple inputs per test, you can consider data driven tests which group each set of inputs together. Something like:
Copy code
class Test : FunSpec() {
   init {
      test("should call service once") {
         forAll(
            row("1", true, 5.4),
            row("2", false, 14.2)
         ) { a, b, c ->
            val testService: TestService = mockk(relaxed = true)
            every {
               testService.test(any())
            } returns "foo"
            testService.test(a, b, c)
            verify(exactly = 1) { testService.test(any()) }
         }
      }
   }

   class TestService {
      fun test(string: String, boolean: Boolean, double: Double): String {
         return string
      }
   }
}
h

huehnerlady

07/27/2020, 5:55 AM
@sam many thanks. this looks awesome. I actually started testig kotest last week and I love it. I am going to migrate all tests now, thanks for your help 🙂
You used the FunSpeck. There are so many different specs, so I was wondering, why are you using FunSpec? 🙂 I had a look and so far FreeSpec looks very appealing to me, so just wanted to make sure that there isn’t anything important I miss as a difference 🙂
s

sam

07/27/2020, 6:56 AM
It's just preference really.
h

huehnerlady

07/27/2020, 7:07 AM
Ah ok, thanks 🙂
4 Views