Uploaded image for project: 'Camel'
  1. Camel
  2. CAMEL-16658

AmbiguousMethodCallException when using Mockito mock as bean for camel-bean component.

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Minor
    • Resolution: Fixed
    • 3.x
    • 3.7.5, 3.11.0
    • camel-bean
    • 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);
          }
      
      }
      

      Attachments

        Activity

          People

            davsclaus Claus Ibsen
            kmackowiak Krzysztof Mackowiak
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: