Details
-
Bug
-
Status: Resolved
-
Minor
-
Resolution: Fixed
-
3.x
-
None
-
Unknown
Description
In Camel 2.x camel-bean component considered only public methods when trying to choose a method to call.
In Camel 3.x this behaviour was changed and now protected and package methods are also considered (it was implemented in the CAMEL-13298 task).
Unfortunately this change causes that using Mockito mocks as beans for camel-bean components (for example to test some Camel routes) causes an AmbiguousMethodCallException to be thrown.
Here is an example of such exception:
org.apache.camel.component.bean.AmbiguousMethodCallException: Ambiguous method invocations possible: [public java.lang.String org.apache.camel.component.bean.MockitoMockForClassTest$MyService$MockitoMock$1368359973.doSomething(java.lang.String), final java.lang.String org.apache.camel.component.bean.MockitoMockForClassTest$MyService$MockitoMock$1368359973.doSomething$accessor$MJMz0OHK(java.lang.String)] on the exchange: Exchange[] at org.apache.camel.component.bean.BeanInfo.chooseBestPossibleMethodInfo(BeanInfo.java:887) ...
This is caused by the fact that Mockito adds package level methods with the same argument list to created mock/spy.
Additional complication is added by the fact that Mockito 1.x (which uses CGLIB underneath) adds different methods than Mockito 2.x and 3.x (which uses Byte Buddy).
Here are 3 JUnit tests that reproduce this problem:
// NOTE: fails for Mockito 3.x and 2.x, but passes for Mockito 1.x public class MockitoMockForClassTest extends ContextTestSupport { @Test public void testCallingMock() throws Exception { Object response = template.requestBody("direct:start", "anything"); assertEquals("mocked answer", response); } @Override protected Registry createRegistry() throws Exception { MyService mockService = Mockito.mock(MyService.class); when(mockService.doSomething(any())).thenReturn("mocked answer"); Registry answer = super.createRegistry(); answer.bind("myService", mockService); return answer; } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { public void configure() throws Exception { from("direct:start").bean("bean:myService"); } }; } public class MyService { public String doSomething(String body) { return "real answer"; } } }
// NOTE: fails for Mockito 3.x and 2.x, but passes for Mockito 1.x public class MockitoSpyForClassTest extends ContextTestSupport { @Test public void testCallingSpy() throws Exception { Object response = template.requestBody("direct:start", "anything"); assertEquals("mocked answer", response); } @Override protected Registry createRegistry() throws Exception { MyService mockService = Mockito.spy(new MyService()); when(mockService.doSomething(any())).thenReturn("mocked answer"); Registry answer = super.createRegistry(); answer.bind("myService", mockService); return answer; } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { public void configure() throws Exception { from("direct:start").bean("bean:myService"); } }; } public class MyService { public String doSomething(String body) { return "real answer"; } } }
// NOTE: fails for Mockito 1.x, but passes for Mockito 3.x and 2.x public class MockitoMockForInterfaceTest extends ContextTestSupport { @Test public void testCallingMock() throws Exception { Object response = template.requestBody("direct:start", "anything"); assertEquals("mocked answer", response); } @Override protected Registry createRegistry() throws Exception { MyService mockService = Mockito.mock(MyService.class); when(mockService.doSomething(any())).thenReturn("mocked answer"); Registry answer = super.createRegistry(); answer.bind("myService", mockService); return answer; } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new RouteBuilder() { public void configure() throws Exception { from("direct:start").bean("bean:myService"); } }; } public interface MyService { String doSomething(String body); } }