5

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
  • Are you using another @Profile for your tests? How do you define Myinterface as a mock? Commented Apr 4, 2020 at 8:45

2 Answers 2

4
+50

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" } 
Sign up to request clarification or add additional context in comments.

Comments

1

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.

5 Comments

@Fred..Thanks for response but it's calling actual Implementation. Ideally when mocking or stubbing has been done then actual implementation shouldn't be get call..But thanks agian..
That's definitely not possible. First it's an interface, so it can't be calling the actual implementation and second, mock does not call the real implementation ever. You'd have to use a spy and actually create the instance yourself. Please check that there are no another things interfering, such as the Autowired annotation.
@jhon actually reading what auto wired does, it sounds like if you keep it in the test it'll inject your mock with the defined implementation. I don't think you want that, so I guess you want to remove that annotation?
The answer seems correct, but it's also necessary to add the annotation @InjectMocks to the controller in the test, see stackoverflow.com/q/16467685/6245535
@Loris Securo that might be true. I just didn't see any instance of the controller in the code so wasn't sure where the mock was going. I can update the answer if I know exactly how the mock should be used

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.