mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.1k stars 200 forks source link

Multiple verify does not work #392

Open Zwaartekracht opened 3 years ago

Zwaartekracht commented 3 years ago

Hi,

my tests look like

package com.example.wichtelquiz.framework

import com.example.wichtelquiz.edm.Frage import com.nhaarman.mockitokotlin2. import org.junit.After import org.junit.Assert. import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.internal.invocation.MockitoMethod import org.mockito.internal.verification.Times

class SpielerTest { lateinit var wichtel: Spieler

@Before
fun vorJedemTest() {
    wichtel = Spieler("Wichtel")
}

@After
fun nachJedemTest() {

}

@Test
fun constructor () {
    assertEquals("Wichtel", wichtel.name)
    assertEquals(0, wichtel.level)
    assertEquals(3, wichtel.jokers.size)
    assertEquals( false, Joker.PERSON in wichtel.jokers)
    assertNull(wichtel.aktuelleFrage)

    var knummi = Spieler("Knummi", true)
    assertEquals(knummi.name, "Knummi")
    assertEquals(knummi.level, 0)
    assertEquals(knummi.jokers.size, 4)
    assertEquals(Joker.PERSON in knummi.jokers, true)
}

@Test
fun jokerBeiKeinerFrage() {
    try {
        wichtel.jokerEinsetzen(Joker.ANRUF)
    } catch (ex: KeineFrage ) {
        assertNotNull(ex)
    }

    assertEquals(Joker.ANRUF in wichtel.jokers, true)
}

@Test
fun jokerWeg() {
    var mock : Frage = mock()
    wichtel.aktuelleFrage = mock
    wichtel.jokerEinsetzen(Joker.ANRUF)

    verify(mock).jokerEinsetzen(Joker.ANRUF)

    assertEquals(Joker.ANRUF in wichtel.jokers, false)
}

@Test
fun falscherJoker() {
    wichtel.aktuelleFrage = mock()
    try {
        wichtel.jokerEinsetzen(Joker.PERSON)
    } catch (ex: FalscherJoker) {
        assertNotNull(ex)
        assertEquals(ex.joker, Joker.PERSON)
    }

    wichtel.jokerEinsetzen(Joker.FUENFZIGFUENFZIG)
    try {
        wichtel.jokerEinsetzen(Joker.FUENFZIGFUENFZIG)
    } catch (ex: FalscherJoker) {
        assertNotNull(ex)
        assertEquals(ex.joker, Joker.FUENFZIGFUENFZIG)
    }
}

@Test
fun frageLesen () {
    var fragenMock: Frage = mock()

    wichtel.frageLesen(fragenMock)

    assertSame(fragenMock, wichtel.aktuelleFrage)
}

@Test
fun antworten() {
    var mock : Frage = mock()
    wichtel.aktuelleFrage = mock

    wichtel.antworten(2)

    verify(mock).antworten(2)

}

}

running jokerWeg alone is green but as soon as I run the complete classe jokerWeg gets red.

org.mockito.exceptions.misusing.UnfinishedVerificationException: Missing method call for verify(mock) here: -> at com.nhaarman.mockitokotlin2.VerificationKt.verify(Verification.kt:42)

Example of correct verification: verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified. Mocking methods declared on non-public parent classes is not supported.

at com.example.wichtelquiz.framework.SpielerTest.jokerWeg(SpielerTest.kt:115)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Process finished with exit code -1

Can you help what I am missing here? Version is 2.8.5

Cheers Silke

bohsen commented 3 years ago

@Zwaartekracht Need some more context to be able to help

at com.example.wichtelquiz.framework.SpielerTest.jokerWeg(SpielerTest.kt:115) ...

Is this the same as?

var mock : Frage = mock()

What does Frage look like? Did you maybe forget to make Frage open or are you maybe using Mockito-Inline artifact or all-open plugin wrong?

Also at the end you write:

Version is 2.8.5

Mockito-kotlin latest version is 2.2.0

Zwaartekracht commented 3 years ago

Hi sorry you are right:-) androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"

in my gradle ;-) 2.8.5 was my gson.

The Frage class is completely open as running a single test works I told that but running them all together nnot :-)

bohsen commented 3 years ago

@Zwaartekracht Any chance that you could post an example of your test-class with implementations of Spieler and Frage. It doesn't have to be the exact implementation from your project. Just implementations inside the test-class that makes it possible to reproduce.

I also noticed that you use:

androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"

Are you maybe using inline mocking inside an instrumentation test? Because that won't work.

Zwaartekracht commented 3 years ago

Yes they are instrumentation tests as I need a context. You mean mocking an app context instead of instrumentation would work.

bohsen commented 3 years ago

@Zwaartekracht Not sure why jokerWeg pass when run as a single test and not when run all together. To me it sounds as if you have state leaking between your tests.

This is a longshot, but maybe try initializing your Spieler-class without lateinit var and remove the Before and After functions, you don't need them. This ensures that your subject under test (sut) is created from scratch between your tests:

class SpielerTest {

    private val wichtel = Spieler("Wichtel")

    @Before // Remove this function
    fun vorJedemTest() {
        wichtel = Spieler("Wichtel")
    }

    @After // Remove this function
    fun nachJedemTest() {

    }

    ...