How to mock web services

In black box testing, sometimes we need to test a system which consumes and produces web services. Here is my experience on how to test such a system.

you need to call a the exposed web service and check how the consumed webs services are called. Exposed web service is already provided by the system, and you need only a client to call it. I used CXF. very straight forward and easy.

But the consumed web services are not there yet. They are not in the scope of tests and mostly not in our control at all.

One approach is to create fix implementation of thos web services, again using a library like CXF and  when the callback functions are called, check that the parametrs are passed correctly and resturn a suitable result for test of other parts at the same time.

This approach works fine only for the simplest cases. If you want to test different scenarios, the fixed mock web service should react differently and you have to put the logic inside it. Sometimes you meay need to have more than one implementation and re-publish the service with the alternative implementations. Not beautiful I admit.

A more beautiful approach is to use mocking frameworks. Here I have selected Mockito.

Here are the steps:

First you have to create a mock object, a normal one based on the service interface. I do it via annotation. like this:

@Mock
static ServiceInterface serviceMock;

But this should be called from a web service. For any strange reason, it could not be done directly. I had to create a reflecter class to do the job:

@Mock
public class ServiceProxy implements java.lang.reflect.InvocationHandler {
    private Object obj;

    public static Object newInstance(Object obj) {
        return java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            obj.getClass().getInterfaces(),
            new ServiceProxy(obj));
    }


    private ServiceProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method m, Object[] args)
        throws Throwable {
        Object result;
        try {
    
            result = m.invoke(obj, args);
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " +
            e.getMessage());
        }

        return result;
    }
}

then it is easy, you can define your web service mock and start it.


service = (ServiceInterface)         ServiceProxy.newInstance(serviceMock);
serviceEndpoint = Endpoint.publish(
				"http://localhost:8080/myService", service);
 

Every thing is ready now. you can run the system and use the mock, just like a normal mock.
As an example of the usage, this code tests if a method is called in web service. Obviously all the mock language of Mockito can be used for more complex tests.

verify(serviceMock).myMethod(anyInt);

 
Enjoy your testing !

To present it in a more concrete way I have created a sample usage. It could be found here: https://github.com/smirzai/webServiceMock.git

There is also a worldline version. It uses resource locator to find web service.

12 thoughts on “How to mock web services

    1. admin Post author

      Hi Faramarz
      Actually I am doing it in an ESB namely Apache ServiceMix. In Servicemix this was the best solution I could find. Are you referring to a specific ESB ?

      Reply
  1. Parkre

    Hi Saeid –

    did you mean to annotate the class with Mock? JDK8 + Mockito 1.9.5 refuses this.

    Instead, could you mean @RunWith(MockitoJUnitRunner.class) ?

    /Parker

    Reply
    1. admin Post author

      Hi Parker,
      frankly speaking I did not check it with JDK8. It should also work with @RunWith annotation.
      The focus is mostly on the CXF integration part.

      — Saeid

      Reply
      1. Parker

        Hi Saied,

        Thanks for the quick response.

        Given
        – goal is an inbound (consumer) web service (aem eats incoming SOAP messages);
        – given a client generated by CXF from wsdl;

        May I ask your input on two things:
        – what is the AEM/CQ way to use this data – what is the hand off to a service impl? I guess I am asking for an instructive link or example;
        – is there a service endpoint I can “see” from a SOAP UI tool? Or, is the service class mentioned above the implicit access point (similar to a servlet).

        Thanks in advance for the pointers.

        /Parker

        Reply
        1. admin Post author

          Hi
          I do not get what do you mean by AEM/CQ. Sorry !
          The service is a real service and of course you can check it with soap UI if it lasts. With my example on gihub, the service is only up during the test case execution and not really measurable. You need to put a wait somewhere.

          Reply
  2. Anju

    javax.xml.ws.WebServiceException: org.apache.ws.commons.schema.XmlSchemaException: Unable to locate imported document at ‘http://ws-i.org/profiles/basic/1.1/swaref.xsd’, relative to ‘schema2.xsd’.
    at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:371)
    at org.apache.cxf.jaxws.EndpointImpl.publish(EndpointImpl.java:251)
    at wsmockdemo.maxiTest.testLogicalOperationsWithMock(maxiTest.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
    Caused by: org.apache.ws.commons.schema.XmlSchemaException: Unable to locate imported document at ‘http://ws-i.org/profiles/basic/1.1/swaref.xsd’, relative to ‘schema2.xsd’.
    at org.apache.cxf.catalog.CatalogXmlSchemaURIResolver.resolveEntity(CatalogXmlSchemaURIResolver.java:76)
    at org.apache.ws.commons.schema.SchemaBuilder.resolveXmlSchema(SchemaBuilder.java:684)
    at org.apache.ws.commons.schema.SchemaBuilder.handleImport(SchemaBuilder.java:538)
    at org.apache.ws.commons.schema.SchemaBuilder.handleSchemaElementChild(SchemaBuilder.java:1513)
    at org.apache.ws.commons.schema.SchemaBuilder.handleXmlSchemaElement(SchemaBuilder.java:659)
    at org.apache.ws.commons.schema.SchemaBuilder.build(SchemaBuilder.java:157)
    at org.apache.ws.commons.schema.XmlSchemaCollection.read(XmlSchemaCollection.java:497)
    at org.apache.ws.commons.schema.XmlSchemaCollection.read(XmlSchemaCollection.java:483)
    at org.apache.cxf.common.xmlschema.SchemaCollection.read(SchemaCollection.java:133)
    at org.apache.cxf.databinding.AbstractDataBinding.addSchemaDocument(AbstractDataBinding.java:193)
    at org.apache.cxf.databinding.AbstractDataBinding.addSchemaDocument(AbstractDataBinding.java:96)
    at org.apache.cxf.jaxb.JAXBDataBinding.initialize(JAXBDataBinding.java:398)
    at org.apache.cxf.service.factory.AbstractServiceFactoryBean.initializeDataBindings(AbstractServiceFactoryBean.java:86)
    at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.buildServiceFromClass(ReflectionServiceFactoryBean.java:490)
    at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.buildServiceFromClass(JaxWsServiceFactoryBean.java:704)
    at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.initializeServiceModel(ReflectionServiceFactoryBean.java:550)
    at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.create(ReflectionServiceFactoryBean.java:265)
    at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.create(JaxWsServiceFactoryBean.java:215)
    at org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory.createEndpoint(AbstractWSDLBasedEndpointFactory.java:102)
    at org.apache.cxf.frontend.ServerFactoryBean.create(ServerFactoryBean.java:159)
    at org.apache.cxf.jaxws.JaxWsServerFactoryBean.create(JaxWsServerFactoryBean.java:211)
    at org.apache.cxf.jaxws.EndpointImpl.getServer(EndpointImpl.java:456)
    at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:334)
    … 26 more

    Reply
  3. Anju

    your example is working fine for me. also worked with couple of webservices which are not involved any attachment. one of the webservice is having attachments “http://ws-i.org/profiles/basic/1.1/swaref.xsd”

    and getting errro. tried to add the swaref.xsd on classpath and metainf folders but still getting error

    javax.xml.ws.WebServiceException: org.apache.ws.commons.schema.XmlSchemaException: Unable to locate imported document at ‘http://ws-i.org/profiles/basic/1.1/swaref.xsd’, relative to ‘schema2.xsd’.
    at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:371)
    at org.apache.cxf.jaxws.EndpointImpl.publish(EndpointImpl.java:251)r

    Reply
    1. admin Post author

      Looks interesting. But looks to use node.js. I guess it is not easy to run the whole test junit tests using this tool

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.