kotlinunit-testingbddmockkkotest

Kotest with Mockk: How to clear verify count


So I have the following code:

When("SMS with location update command is received") {
        every {
            context.getString(R.string.location_sms, any(), any(), any(), any())
        } returns "loc"
        mainServiceViewModel.handleSms(SmsMessage("123", "location"))

        Then("SMS with location is sent to specified phone number") {
            verify(exactly = 1) {
                smsRepository.sendSms("+123", "loc")
            }
        }
    }

    When("motion is detected") {

        Then("information SMS is sent to specified phone number") {
            verify(exactly = 1) {
                smsRepository.sendSms("+123", any())
            }
        }
    }

The problem with it, that both cases pass, even though the second one doesn't do any action. I expect second case to fail, as sendSms method is not even called.

  1. How to reset smsRepository verify count?
  2. How to reset that count before every When case?

Solution

  • This is probably due to the fact that Kotest is different from JUnit in what is considered a test and when a Spec instance is created.

    The default behavior for Kotest is to create a single instance of the Spec per execution. Due to this, your mocks are not getting reset between executions, as you probably have them created at class level.


    To fix this, what you can do is either do the mockk inside the test, or change the isolation mode to something that creates the Spec every time a test is executed.

    The default isolationMode is IsolationMode.SingleInstance. You can change it on the Spec itself by overriding the isolationMode function:

    class MySpec : BehaviorSpec() {
    
        init {
            Given("XX") { 
                Then("YY") // ...
            }
        }
    
        override fun isolationMode() = IsolationMode.InstancePerTest
    
    }
    

    You can also change it in a ProjectConfig. If you need explanation on how to do it there, check the docs on ProjectConfig


    An alternative would be to clear mocks on the afterTest method:

    class MySpec : BehaviorSpec() {
    
        init {
            Given("XX") { 
                Then("YY") // ...
            }
        }
    
        override fun afterTest(testCase: TestCase, result: TestResult) {
            clearAllMocks()
        }
    
    }
    

    But I'm not sure how that would work in your use-case.