No, you don’t need to mock your SOAP Web Service to test it


A short blog about a topic I was discussing last week with a customer: testing SOAP Web Services. If you follow my blog you would know by now that I’m not a fan of unit testing in MOCK environments. Not because I don’t like it or I have religious believes that don’t allow me to use JUnit and Mockito. It’s just because with the work I do (mostly Java EE using application servers) my code runs in a managed environment (i.e. containers) and when I start mocking all the container’s services, it becomes cumbersome and useless. Few months ago I wrote a post about integration testing with Arquillian. But you don’t always need Arquillian to test inside a container because today, most of the containers are light and run in-memory. Think of an in-memory database. An in-memory web container. An in-memory EJB container.

So first, let’s write a SOAP Web Service. I’m using the one I use on my book : a SOAP Web Service that validates a credit card. If you look at the code, there is nothing special about it (the credit card validation algorithm is a dummy one: even numbers are valid, odd are invalid). Let’s start with the interface :

import javax.jws.WebService;

@WebService
public interface Validator {
    public boolean validate(CreditCard creditCard);
}

Then the SOAP Web Service implementation :

@WebService(endpointInterface = "org.agoncal.book.javaee7.chapter21.Validator")
public class CardValidator implements Validator {

  public boolean validate(CreditCard creditCard) {

    Character lastDigit = creditCard.getNumber().charAt(creditCard.getNumber().length() - 1);

    return Integer.parseInt(lastDigit.toString()) % 2 != 0;
  }
}

What do you need to test this SOAP Web Service ? Nothing ! The code above is just a POJO so you can write a unit test that checks the validation algorithm. Something as follow :

public class CardValidatorTest {

  @Test
  public void shouldCheckCreditCardValidity() {

    CardValidator cardValidator = new CardValidator();

    CreditCard creditCard = new CreditCard("12341234", "10/10", 1234, "VISA");

    assertFalse("Credit card should be valid", cardValidator.validate(creditCard));
    creditCard.setNumber("12341233");
    assertTrue("Credit card should not be valid", cardValidator.validate(creditCard));
  }
}

In this unit test I instantiate the CardValidator class and invoke the validate method. This is acceptable, but what if your SOAP Web Serivce uses Handlers ? What if it overrides mapping with the webservice.xml deployment descriptor ? Uses the WebServiceContext ? In short, what if your SOAP Web Service uses containers’ services ? Unit testing becomes useless. So let’s test your SOAP Web Service inside the container and write an the integration test. For that we can use an in-memory web container. And I’m not just talking about a GlassFish, JBoss or Tomcat, but something as simple as the web container that come with the SUN’s JDK. Sun’s implementation of Java SE 6 includes a light-weight HTTP server API and implementation : com.sun.net.httpserver.

Note that this default HTTP server is in a com.sun package. So this might not be portable depending on the version of your JDK. Instead of using the default HTTP server it is also possible to plug other implementations as long as they provide a Service Provider Implementation (SPI) for example Jetty’s J2se6HttpServerSPI.

So this is how an integration test using an in memory web container can look like :

public class CardValidatorIT {

  @Test
  public void shouldCheckCreditCardValidity() throws MalformedURLException {
    // Publishes the SOAP Web Service
    Endpoint endpoint = Endpoint.publish("http://localhost:8080/cardValidator", new CardValidator());
    assertTrue(endpoint.isPublished());
    assertEquals("http://schemas.xmlsoap.org/wsdl/soap/http", endpoint.getBinding().getBindingID());

    // Data to access the web service
    URL wsdlDocumentLocation = new URL("http://localhost:8080/cardValidator?wsdl");
    String namespaceURI = "http://chapter21.javaee7.book.agoncal.org/";
    String servicePart = "CardValidatorService";
    String portName = "CardValidatorPort";
    QName serviceQN = new QName(namespaceURI, servicePart);
    QName portQN = new QName(namespaceURI, portName);

    // Creates a service instance
    Service service = Service.create(wsdlDocumentLocation, serviceQN);
    Validator cardValidator = service.getPort(portQN, Validator.class);

    // Invokes the web service
    CreditCard creditCard = new CreditCard("12341234", "10/10", 1234, "VISA");

    assertFalse("Credit card should be valid", cardValidator.validate(creditCard));
    creditCard.setNumber("12341233");
    assertTrue("Credit card should not be valid", cardValidator.validate(creditCard));

    // Unpublishes the SOAP Web Service
    endpoint.stop();
    assertFalse(endpoint.isPublished());
  }
}


The Endpoint.publish() method uses by default the light-weight HTTP server implementation that is included in Sun’s Java SE 6. It publishes the SOAP Web Service and starts listening on URL http://localhost:8080/cardValidator. You can even go to http://localhost:8080/cardValidator?wsdl to see the generated WSDL. The integration test looks for the WSDL document, creates a service using the WSDL information, gets the port to the SOAP Web Service and then invokes the validate method. The method Endpoint.stop() stops the publishin of the service and shutsdown the in-memory web server.

Again, you should be careful as this integration test uses the default HTTP server which is in a com.sun package and therefore not portable.

Comments
5 Responses to “No, you don’t need to mock your SOAP Web Service to test it”
  1. Bastien DAVID says:

    Thanks for sharing.

    The integration test method is interesting…

    However I would not recommend the use of integration tests instead of unit tests for this kind of piece of code (I think you can make the best of both). I do believe you are doing too many things in the CardValidator class, breaking the single responsibility pattern (which is why one may be tempted not to write unit tests).

    I would instead write a CardValidator that does only the card validation (and I would rather write the unit tests of this class first), and that I can reuse anytime in other contexts, without any dependency on webservice libraries.

    Once my CardValidator passes all the tests, I would then encapsulate it in a CardValidatorService, and then, I would write the Integration test you wrote (though I would likely mock my CardValidator, and just make sure my Service is really relying on it to do the job – no need to check the validation is OK, the CardValidator tests / specifications are here for that and I just want to test my service itself).

    I would really like to know what you think of this approach.

    Kind regards.

    • agoncal says:

      To be honest I am a bit fed up with encapsulation, layering and other over-architecturing principals. As you say, the example I’m giving here is trivial. So why complicate it ? Why introducing interfaces or any other class. I like to keep things simple when I can.

  2. If you @inject some beans or even if you use inject an @ejb into the webservice is this still true? if isnt how is the way to test it?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 8,088 other followers

%d bloggers like this: