6 Replies Latest reply on Apr 8, 2014 3:23 AM by Jorge Morales

    How to mock 2 invocations in same method

    Jorge Morales Master

      Hi,

      I have a Bean Component from where I do 2 invocations to same method, with different values as parameters and another use case where I have a method that calls a reference with 2 different methods, in the scope of the same Bean component's method. How can I mock it?

       

      This question is needed for a project I'm involved in as well as a document I'm writing regarding JUnit in SwitchYard, witch tips and tricks. http://unpoucode.blogspot.com/2014/03/junit-in-switchyard.html

       

      public class BeanA {
        @Inject @Reference private myReferencedService;
      
         public void execute(...){
           ...
           String a = myReferencedService.test(paramsA);
           ...
           String b = myReferencedService.test(paramsB);
           ...
        }
      }
      
      

       

       

      public class BeanA {
        @Inject @Reference private myReferencedService;
      
      
         public void execute(...){
           ...
           String a = myReferencedService.methodA(paramsA);
           ...
           String b = myReferencedService.methodB(paramsB);
           ...
        }
      }
      
      
      
      

       

      I want to mock the invocations to this methods in myReferencedService, but I haven't been able.

      I have tried different options, with no success, like creating a new ExchangeHandler and trying to figure in the handleMessage which invocation it is or which methoid has been invoked.

      I have also tried to register 2 different operations for the service being called, for different operations signature, and it doesn't also work.

       

          @Test
          public void test() {
              final QName serviceName = new QName(
                      "urn:com.example.switchyard:stringtemplate-example:1.0",
                      "TestMethod");
      
      
              for (Service s : testKit.getServiceDomain().getServices()) {
                  System.out.println(s);
              }
      
      
              testKit.replaceService(serviceName, new ExchangeHandler() {
                  @Override
                  public void handleMessage(Exchange exchange)
                          throws HandlerException {
                      Message mess = exchange.createMessage();
                      mess.setContent("Jorge");
                      exchange.send(mess);
                  }
      
      
                  @Override
                  public void handleFault(Exchange exchange) {
      
      
                  }
              });
      
      
              SubscriptionRequest req = new SubscriptionRequest();
      
      
              SubscriptionResponse res = serviceOperationInvocation.sendInOut(req)
                      .getContent(SubscriptionResponse.class); // Expect Element as
                                                                  // transformation is
                                                                  // for XML
      
      
              Assert.assertEquals("Jorge", res.getItem().get(0).getCompanyId());
          }
      
      
          @Test
          public void testInvocation2Services() {
              final QName serviceName = new QName(
                      "urn:com.example.switchyard:stringtemplate-example:1.0",
                      "TestMethod");
      
      
              final QName serviceReferenceName = new QName(
                      "urn:com.example.switchyard:stringtemplate-example:1.0",
                      "testJAXBBean/TestMethod");
      
      
              for (Service s : testKit.getServiceDomain().getServices()) {
                  System.out.println(s);
              }
              
              ExchangeHandler testHandler = new ExchangeHandler() {
                  @Override
                  public void handleMessage(Exchange exchange)
                          throws HandlerException {
                      System.out.println("---- testHandler");
                          Message mess = exchange.createMessage();
                          mess.setContent("Jorge");
                          exchange.send(mess);
                  }
      
      
                  @Override
                  public void handleFault(Exchange exchange) {
      
      
                  }
              };
              
              ExchangeHandler holaHandler = new ExchangeHandler() {
                  @Override
                  public void handleMessage(Exchange exchange)
                          throws HandlerException {
                      System.out.println("---- holaHandler");
                          Message mess = exchange.createMessage();
                          mess.setContent("Hola");
                          exchange.send(mess);
                  }
      
      
                  @Override
                  public void handleFault(Exchange exchange) {
      
      
                  }
              };
              InOutOperation testMethodContract = new InOutOperation("test", 
                      JavaTypes.toMessageType(String.class),   // input
                      JavaTypes.toMessageType(String.class), null);  // output, no-fault
              InOutOperation holaMethodContract = new InOutOperation("hola", 
                      JavaTypes.toMessageType(String.class),   // input
                      JavaTypes.toMessageType(String.class), null);  // output, no-fault
              
      //        testKit.removeService(serviceName);
              testKit.getDeployment().getDomain().registerServiceReference(serviceReferenceName, new InOutService(testMethodContract), testHandler);
              testKit.getDeployment().getDomain().registerServiceReference(serviceReferenceName, new InOutService(holaMethodContract), holaHandler);
      //        testKit.registerInOutService("TestMethod", holaHandler, new InOutService(holaMethodContract));
      
      
              
              SubscriptionRequest req = new SubscriptionRequest();
      
      
              SubscriptionResponse res = serviceOperationInvocation.sendInOut(req)
                      .getContent(SubscriptionResponse.class); // Expect Element as
                                                                  // transformation is
                                                                  // for XML
      
      
              Assert.assertEquals("Jorge", res.getItem().get(0).getCompanyId());
      
      
              SubscriptionRequest req2 = new SubscriptionRequest();
              req2.setCompanyId("Hola");
      
      
              SubscriptionResponse res2 = serviceOperationInvocation.sendInOut(req2)
                      .getContent(SubscriptionResponse.class); // Expect Element as
                                                                  // transformation is
                                                                  // for XML
      
      
              Assert.assertEquals("Hola", res2.getItem().get(0).getCompanyId());
          }
      
        • 1. Re: How to mock 2 invocations in same method
          Keith Babo Master

          Can you elaborate on what's not working?  Replacing a service with a mock is certainly possible, we do it all over the place in our test suite.  If you are saying that you want to register two mocks, one for each operation on a service, that's not going to work.  You can replace a service and then two something different in each operation.

          • 2. Re: How to mock 2 invocations in same method
            Jorge Morales Master

            Hi Keith,

            I think it is clear what we need. Either two mocks, one for each operation, or one mock, but have the ability to know in the mock what invocation it is, so it can return what is expected.

             

            I'm trying with an EventCounter, so I can count the invocation and return, but I see this as a nasty workaround.

             

            I can not do as you suggest, as invocations to the referenced service occurs in the context of the same invocation of the method of the class under test, so I can not do anything there.

             

            I've raised a JIRA ([SWITCHYARD-2041] Make it possible to mock 2 different operations with one mock - JBoss Issue Tracker) as it is very painful not being able to do proper unit tests. I know also other workarounds, but these makes change our logic, and I do not think logic should be mandated by a lack of testing capabilities.

             

            Any proposed workaround?

            • 3. Re: How to mock 2 invocations in same method
              Keith Babo Master

              It may be clear to you, but I'm afraid it's not clear to me at all.  We're happy to get enhancement requests for the project, but it's unclear from your original description what the problem is here.  I don't see why we would support a mock per operation as mocks are necessarily test code and you can cater the logic in the mock based on the operation being invoked.  Maybe you could attach a test project which includes the service you are trying to test along with the switchyard.xml? 

              • 4. Re: Re: How to mock 2 invocations in same method
                Jorge Morales Master

                Hi Keith,

                Thanks for your support, and sorry if it seems confusing to you, as usually one has everything is his head, and maybe is not good at explaining it.

                 

                I can not copy real code, but I can provide with explanation an a sample project.

                 

                I have a switchyard service with two components, ComponentA and ComponentB.

                <?xml version="1.0" encoding="UTF-8"?>
                <sy:switchyard xmlns:bean="urn:switchyard-component-bean:config:1.1" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" xmlns:sy="urn:switchyard-config:switchyard:1.1" name="junit-2methods-example" targetNamespace="urn:com.example.switchyard:junit-2methods-example:1.0">
                  <sca:composite name="junit-2methods-example" targetNamespace="urn:com.example.switchyard:junit-2methods-example:1.0">
                    <sca:component name="ComponentB">
                      <bean:implementation.bean class="com.example.switchyard.junit_2methods_example.ComponentBBean"/>
                      <sca:service name="ComponentB">
                        <sca:interface.java interface="com.example.switchyard.junit_2methods_example.ComponentB"/>
                      </sca:service>
                    </sca:component>
                    <sca:component name="ComponentABean">
                      <bean:implementation.bean class="com.example.switchyard.junit_2methods_example.ComponentABean"/>
                      <sca:service name="ComponentA">
                        <sca:interface.java interface="com.example.switchyard.junit_2methods_example.ComponentA"/>
                      </sca:service>
                      <sca:reference name="ComponentB">
                        <sca:interface.java interface="com.example.switchyard.junit_2methods_example.ComponentB"/>
                      </sca:reference>
                    </sca:component>
                  </sca:composite>
                </sy:switchyard>
                

                ComponentA has ComponentB as a reference.

                 

                Contract for Component B is:

                public interface ComponentB {
                  public String methodA(String s);
                  public String methodB(String s);
                }
                

                 

                And implementation is:

                @Service(ComponentB.class)
                public class ComponentBBean implements ComponentB {
                
                
                  @Override
                  public String methodA(String s) {
                  return "Hello " + s;
                  }
                
                
                  @Override
                  public String methodB(String s) {
                  return s + " world!!!";
                  }
                }
                
                
                
                
                

                 

                 

                Implementation for ComponentA is:

                @Service(ComponentA.class)
                public class ComponentABean implements ComponentA {
                
                
                  @Inject
                  @Reference
                  private ComponentB serviceB;
                
                
                  @Override
                  public String sayHelloWorld(String s) {
                  String stepA = "";
                  String stepB = "";
                  try {
                     stepA = serviceB.methodA(s);
                  } catch (Exception e) {
                  }
                  try {
                    stepB = serviceB.methodB(stepA);
                  } catch (Exception e) {
                  }
                  return stepB;
                  }
                
                
                }
                
                
                
                
                

                 

                 

                I have a test case, where I want to mock invocation to methodA in componentB and throw an Exception, and have different behaviour for invocation on methodB, so I can have:

                 

                    @Test
                    public void testWithMocks_ExceptionInMethodA() throws Exception {
                        String message = "Jorge";
                        ExchangeHandler myHandler = new ExchangeHandler() {
                            
                            @Override
                            public void handleMessage(Exchange exchange) throws HandlerException {
                                throw new HandlerException("Error in methodA");
                            }
                            
                            @Override
                            public void handleFault(Exchange exchange) {
                                
                            }
                        };
                        
                        testKit.replaceService(new QName("urn:com.example.switchyard:junit-2methods-example:1.0","ComponentB"), myHandler);
                        String result = service.operation("sayHelloWorld").sendInOut(message)
                                .getContent(String.class);
                
                
                        // validate the results
                        Assert.assertEquals(" world!!!", result);
                    }
                
                
                
                

                 

                 

                As you can see, I can not honour the Assert, as the mock goes for both methodA and methodB invocation on compoenentB. I would like to be able to register two different mocks, one for ComponentB.methodA and another for ComponentB.methodB, or be able to do something like the following in the registered ExchangeHandler:

                if (exchange.getCalledMethod.equals("methodA"){
                  return new HandlerException("");
                else
                  return " world!!!";
                

                 

                 

                Is it more clear?

                 

                I attach sample project.

                • 5. Re: How to mock 2 invocations in same method
                  Jorge Morales Master

                  Just in case anyone is interested.

                  I have solved my problem (not how I liked) with a reduced switchyard.xml in config for SwitchYardTestCaseConfig. In this alternative composite, I have changed the implementation bean with a mocked one.

                   

                  Still, I think it would be good to have support for registering ExchangeHandlers for different operations, and to be able to have context information for invocation, just to know it it is first call of ExchangeHandler, second, ...

                  1 of 1 people found this helpful
                  • 6. Re: Re: How to mock 2 invocations in same method
                    Jorge Morales Master

                    Hi,

                    Finally I've been able to get which operation is being called on the registered ExchangeHandler, so I can return different return values, as this:

                     

                    @Override
                    public void handleMessage(Exchange exchange) throws HandlerException {
                        if (exchange.getContract().getConsumerOperation().getName().equals("")){
                            throw new HandlerException("Error in methodA");
                        }else{
                            exchange.send(exchange.createMessage().setContent(" world!!!"));
                        }
                    }
                    

                     

                    The only thing I do not know is if I should get the Consumer or the Provider, as both seem the same in the context of my example.