Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Argument matcher check called twice when the mocked method was only called once. #532

Open
roy-t opened this issue Nov 28, 2024 · 1 comment

Comments

@roy-t
Copy link

roy-t commented Nov 28, 2024

When I was writing a test that required a more complex argument matcher I resorted to using check. To my surprise check is evaluated twice when the mocked method was only called once. I believe this is a bug as I think it is reasonable to assume that if a method is called once the argument matchers for each argument of that method are also only called once. Otherwise you might run into problems (as I did) if the arguments can change state.

I managed to get the reproduction down to this, but I did not get much wiser from the stack trace or mockito-kotlin source code to get any further insights.

import org.junit.jupiter.api.Test
import org.mockito.kotlin.check
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import java.util.concurrent.atomic.AtomicInteger
 
class Tests {       
    @Test
    fun testCase() {
        // Create something that we can mock, note counter is not touched
        class Foo {
            fun bar(counter: AtomicInteger) {}
        }

        // Create the mock, again the counter is not touched
        val mocked =
            mock<Foo> {
            }

        val counter = AtomicInteger(0)

        // Call bar exactly once, verify that the counter is still untouched
        mocked.bar(counter)
        assertThat(counter.get()).isEqualTo(0)

        // Verify that bar was indeed called exactly once  
        verify(mocked, times(1))
            .bar(
                // Check how many times the argument matcher runs, by increment the counter each time
                check { it.incrementAndGet() },
            )

        // We would expect counter to now be 1, but it is actually 2, so this assert fails.
        assertThat(counter.get()).isEqualTo(1)
    }
 }

I'm using the following dependencies:

  • mockito-kotlin 5.4.0
  • junit 5.10.3
@tschut
Copy link

tschut commented Nov 28, 2024

I think this is caused by Times::verify, which first checks if there are any invocations at all before checking the number of invocations:

    @Override
    public void verify(VerificationData data) {
        List<Invocation> invocations = data.getAllInvocations();
        MatchableInvocation wanted = data.getTarget();

        if (wantedCount > 0) {
            checkMissingInvocation(data.getAllInvocations(), data.getTarget());
        }
        checkNumberOfInvocations(invocations, wanted, wantedCount);
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants