Glen Mazza's Weblog

« Compressing SOAP... | Main | Using SAAJ to call... »

https://glenmazza.net/blog/date/20180505 Saturday May 05, 2018

Creating integration tests for SOAP web services

See this entry for testing SOAP clients.

My intro web service tutorial already provides JUnit unit tests for checking that the business logic of the web service is accurate before testing SOAP calls with it. For the case of the DoubleIt web service, that involves just a direct Java call to the web service's DoubleIt method and making sure that the number returned is indeed twice that passed in.

The next stage involves integration testing of the web service by making actual SOAP calls to it, making sure SOAP messages are working as intended. Discussed below are some of the options that can be used for making these tests:

  1. Java Endpoint class
  2. embedded Jetty
  3. embedded Tomcat
  4. standalone servlet container (Tomcat)
  5. local transport - a non-TCP/IP option for testing web services

The finished tutorial source code can be obtained from GitHub by using either the "Clone or download" button or git clone -v git://github.com/gmazza/blog-samples.git command. Note in the download the group and artifact ID's listed in the poms have been changed to avoid conflicting with the original tutorial, also much of the dependency and plugin configuration information has been centralized into a higher-level POM, so you might wish to follow along with the finished source code.

To start, add the following abstract test class to new folder DoubleIt/service/src/test/java/service (which follows the Maven standard directory layout). It contains the five test cases that will be called by the concrete deployment-specific test classes below. Each test case illustrates a different method of making a web service call--check the comments for each type to see which format is most useful for you. Deciding factors can be that some test cases require the wsdl2java/wsimport-generated JAX-WS artifacts to be created and some do not, also some allow for using external source files to load in the SOAP message.

To support two of the test cases, these test files will need to be created at the locations specified:

src/test/resources/fullSOAPMessage.xml:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt">
         <numberToDouble>0</numberToDouble>
      </ns2:DoubleIt>
   </soap:Body>
</soap:Envelope>

src/test/resources/justPayload.xml:

<ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt">
   <numberToDouble>7</numberToDouble>
</ns2:DoubleIt>

For each of the subclasses created below, running mvn clean install from either the DoubleIt or DoubleIt/service folders will cause the test cases to be run, with the service's WAR being created only if no test failures occurred. If failures occur, check the service/target/surefire-reports folder for information about the errors.

  1. Testing SOAP calls with the Endpoint class. The Java Endpoint class creates an embedded server that can be used for hosting the web service without need of an explicit servlet container. Apache CXF internally uses Jetty to implement this class. See this article for more information on the Endpoint class.

    The main advantage of using Endpoint from a Maven/JUnit perspective is that this class works directly with the service implementation bean (DoubleItPortTypeImpl in our example) without need for a WAR file. This allows the tests to be run before the WAR file is created by Maven, which fits well with that tool's build process (in Maven WARs are created after the module's tests are run, and failed tests result in no WAR being generated.)

    First we need to add web stack-specific dependencies that are internally needed by the Endpoint object as it exposes a web service. For CXF, add the following dependency to the CXF profile in the DoubleIt/services/pom.xml file:

    <dependencies>
       <dependency>
          <groupId>org.apache.cxf</groupId>
          <artifactId>cxf-rt-transports-http-jetty</artifactId>
          <version>${cxf.version}</version>
          <scope>test</scope>
       </dependency>
    </dependencies>
    

    Then place this test class in the service/src/test/java/service folder:

  2. Testing SOAP calls with an embedded Jetty instance. Testing with embedded Jetty or Tomcat is perhaps best if you want to more closely duplicate your production environment. It may also be helpful if you wish to test SSL or similar settings available with a servlet container but not with the Endpoint interface.

    Because Jetty (and embedded Tomcat in the next example) require a WAR file or an exploded WAR directory, we'll put these test cases in the war submodule instead, and run them as Maven integration tests (mvn integration-test instead of mvn test). We use integration tests here because they run later in the build cycle after the JAR (or WAR in this case) needed for the tests has already been built. Integration test failures will still halt the install phase, just not the package one as the regular unit tests do.

    Steps involved:

    1. Add the JUnit and Jetty webapp dependencies and the Maven Failsafe plugin to the war submodule's POM file. (In the source code download I have the latter's full definition in the blog-samples' pom.xml as it can be used elsewhere with other samples.) The Failsafe plugin is necessary or else the integration tests will be ignored.

    2. In the war submodule, create src/test/java/service and src/test/resources subdirectories. Place the above-defined DoubleItPortTypeImplTest.java in the former and fullSOAPMessage.xml and justPayload.xml in the latter.

    3. Next, in the war/test/java/service folder place the following EmbeddedJettyIT class ("~IT" instead of "~Test" ending as Maven has integration tests follow a different naming convention to distinguish between the two):

    4. From the project base folder run mvn clean install or just mvn integration-test. All five tests should run cleanly. As an added check you might wish to temporarily alter the test case values to incorrect values and re-run to confirm the test cases will fail.

  3. Testing SOAP calls with an embedded Tomcat instance. Tomcat's Tomcat class JavaDoc provides more information on using embedded Tomcat, Heroku article and Mark Thomas' ApacheCon presentation is also useful. This process is much the same as embedded Jetty--just follow the instructions above with these exceptions:

    1. Instead of the Jetty dependency listed in war/pom.xml, add the test-scope Tomcat dependencies listed in the source code download. Note the tomcat-embed-jasper dependency I included isn't really needed as we're not using JSP but many no-JSP-available warnings will appear without it.

    2. Tomcat's version of the test class is below. Note for tutorial purposes I have the embedded Tomcat IT test running on port 9002 and the earlier Jetty one on port 9001 so the integration tests for both can run at the same time.

  4. Testing SOAP calls with a standalone servlet container. Maven doesn't provide a phase for testing something after it's already been deployed to a standalone servlet container, so this type of testing would in some ways be nonideal, however this method can be used to more closely approximate your production environment. Here, just providing the endpoint URL would be needed, with none of the Jetty or Tomcat test-scope dependencies listed above necessary. The source code has an inactive StandaloneTomcatIT.txt copy of the class below in the war submodule's test folder along with the prior embedded test classes, renaming it to .java will allow it to be used. However, because the tests cannot run until the web service has already been deployed, you'll probably want to keep such a test class in a Maven project separate from the one deploying the web service.

    package service;
    
    import java.net.URL;
    import org.junit.BeforeClass;
    
    public class StandaloneTomcatTest extends DoubleItPortTypeImplTest {
    
       @BeforeClass
       public static void setUp() throws Exception {
          wsdlURL = new URL("http://localhost:8080/doubleit/services/doubleit?wsdl");
       }
    }
    
  5. Testing SOAP calls using the (non-TCP/IP) local transport option. I have not tried this method, but it can be advantageous if you would like to avoid opening up ports while running your JUnit tests. On the other hand, as such testing would use a transport different from what you would be using in production, this type of testing might be less reliable. Information on using local transport calls for CXF is here.

JUnit Notes

  • JUnit offers a Parameterized runner that provides a streamlined way of making the same SOAP call with different request values.
  • You can use the expected element of the @Test annotation for testing that expected SOAP Faults will occur, i.e., @Test(expected=DoubleItValueException.class).
  • Another JUnit option is the timeout annotation on the Test tag. This will fail any test that does not complete within the specified number of milliseconds and can be used to test web service response time.

Comments

Post a Comment: