首页 > 解决方案 > org.mockito.exceptions.misusing.WrongTypeOfReturnValue while stubbing a function from a "spy" object with mockito in groovy

问题描述

I'm having this issue which has taken enough time from me so I decided to ask the experts here. Some context.. I have a Jenkins shared library which with some groovy classes and I'm writing some unit tests using the JenkinsPipelineUnit framework. I'm using normal junit for testing so nothing special there. Basically I have a groovy class as following:

class MyClass {
    // In reality, `run` is object from the final class WorkflowRun but in the tests, I pass here a dummy map with the important fields
    def run

    MyClass(def run) {
        this.run = run
    }

    boolean somethingExists() {
        // some code here that does some stuff on `run` and return a boolean
    }

    String doSomething() {
        if(somethingExists()) {
            // do something with it.
            return "foo"
        } else {
            // do something else
            return "bar"
        }
    }
}

The code above is simple enough and I am writing the following test for the doSomething() function without having to mock all the internals of somethingExists().

@Test
void testDoSomething() {
    Map run = [number: 1]
    MyClass mc = spy(new MyClass(run))
    // The problem is with the next line.
    doReturn(true).when(mc).somethingExists()

    assertTrue(mc.somethingExists())
    assertEquals("foo", mc.doSomething())
}

Note that I cannot mock the WorkflowRun class since it is a final class and honestly I don't care about it since I need to test the logic inside doSomething() rather than what somethingExists() does which is checking some Jenkins internals on the run object. Therefore, I decided to "spy" on the object under test mc and make it return a simple true or false when I call somethingExists() to avoid too much mocking of the run object. According to Mockito's documentation, when I use the spy function, I should use the stubbing format like I did doReturn(true).when(mc).somethingExists() unlike the other format of when(mc.somethingExists()).thenReturn(true) since this will actually call the real function on the class while attempting to stub it.

Error message: When I call the doReturn(true).when(mc).somethingExists() I get the following error

org.mockito.exceptions.misusing.WrongTypeOfReturnValue: 
Boolean cannot be returned by getMetaClass()
getMetaClass() should return MetaClass
***
If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. This exception *might* occur in wrongly written multi-threaded tests.
   Please refer to Mockito FAQ on limitations of concurrency testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies - 
   - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.

    at org.codehaus.groovy.runtime.callsite.CallSiteArray.createPogoSite(CallSiteArray.java:140)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallSite(CallSiteArray.java:158)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
    at MyClassTest.testDoSomething(MyClassTest.groovy:37)
...

Note that I'm not running tests in parallel (actually while reproducing the issue, I was running only this test) and I'm following the stubbing rule suggested. At first I thought it is because of the doReturn(true) which is returning a primitive type instead of an object so I replaced it with doReturn(Boolean.FALSE) but I still got the same error. When I replace the line doReturn(true).when(mc).somethingExists() with when(mc.somethingExists()).thenReturn(true) then it fails because it is calling the real function mc.somethingExists() which is expected in my opinion. On the other hand, when I replace the entire content of the somethingExists() function with a simple return false, surprisingly, the format doReturn(true).when(mc).somethingExists() still failed however the other format when(mc.somethingExists()).thenReturn(true) worked which I'm surprised because this format should not work for spy objects.

Any clues what I might be doing wrong? I have spent a lot of time trying to find the problem. Thanks in advance.

标签: unit-testingjenkinsgroovymockitojenkins-shared-libraries

解决方案


推荐阅读