问题
I am having a simple controller class
@RestController
open class MyController() {
@Autowired
lateinit var myInterface: MyInterface
@GetMapping(value = ["/v1/call-Api"], produces = ["application/json"])
fun getData():Response{
callFx()
/// Here I have logic
}
fun callFx():String{
return myInterface.getmyStringData()
}
}
Now Come to implementation part of
MyInterface
@Service
class MyImpl: MyInterface {
override fun getmyStringData(){
return "Some string"
}
}
Please note that for MyInterface, I have only one implementation class.
Now come to Test case of controller class
class ControllerTest{
@Autowired
lateinit var myIntF: Myinterface
@Test
fun controllerTest(){
Mockito.`when`(myIntF.getmyStringData()).thenReturn("Some mock string")
// Some code over here
}
}
After all these I am keep getting below error
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
Even though code syntax belongs to Kotlin but i keep it simple to elaborate me scenario. Any JAVA guy can also help me.
Any help would be really helpful for me.
回答1:
The problem:
Below is your test class
class ControllerTest{
@Autowired
lateinit var myIntF: MyInterface
@Test
fun controllerTest(){
Mockito.`when`(myIntF.getmyStringData()).thenReturn("Some mock string")
// Some code over here
}
Since you have used @Autowired
, the real implementation is used and not the mock object and hence when you do Mockito.when(myIntF.getmyStringData()).thenReturn("Some mock string")
, you get the error when() requires an argument which has to be 'a method call on a mock'
. This is because myIntF
is not a mock object.
The solution:
First, since it's a controller test, you need to have the controller field annotated with @InjectMocks
to inject the mocked MyInterface
obj into it. Then you need to annotate the MyInterface
field with @Mock
which would create a mocked object. Then you need to have a @Before
or @BeforeEach
method with MockitoAnnotations.initMocks(this)
to initialize objects annotated with Mockito annotations. Only after that, the method call mocking with Mockito.when(mockedObject.methodCall).thenReturn(mockedValue)
would work.
class ControllerTest{
@InjectMocks
lateinit var controller: MyController
@Mock
lateinit var myIntF: MyInterface
@BeforeEach
fun init() {
MockitoAnnotations.initMocks(this)
}
@Test
fun controllerTest(){
Mockito.`when`(myIntF.getmyStringData()).thenReturn("Some mock string")
// Some code over here
controller.callFx() //this would return "Some mock string"
}
回答2:
I'm not familiar with @Autowire
so this might be a completely wrong assumption, but it's also too big for a comment, so here it goes.
The exception basically explains that the object you're trying to mock isn't a mock and from what I can see, this is true.
Usually one can do something like:
@Mock
lateinit var myIntF: Myinterface
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
And now the mock is created and you can configure it with when
like you have.
There are other options to initialize this, such as running the test with the mockito test runner. I believe there's also a test rule and you'll always have mockito-kotlin which is great for kotlin code and it's much simpler in my personal opinion:
lateinit var myIntF = mock<Myinterface>()
@Test
fun controllerTest(){
myIntF.stub {
on { getmyStringData() } doReturn "Some mock string"
}
}
The point here is that I think you didn't actually create a mock and mockito needs this because if I'm not mistaken it works by inheriting from the class it's mocking.
Edit:
As pointed out in the comments you might want to test the controller. This means you'll need to instantiate it with the mocks you've created. One suggested way is to use @InjectMocks
. Something like:
@InjectMocks
lateinit var controller: MyController
But without knowing the whole test code it's hard to say this is exactly what you want.
来源:https://stackoverflow.com/questions/60950079/org-mockito-exceptions-misusing-missingmethodinvocationexception-in-mockito