Functional boundary testing of a service-based environment using MockServer

0

In big projects hopefully testing the components of the system is a key point. When multiple teams are working on a project and each create services in there functional domain, you want the place the responsibility of these services with that software development team (SDT). Let’s talk in terms of a assembly line. If one team needs to integrate with a service of another team it should not have to wait for the other team to finish implementing the service. Also you don’t want that the team that needs to integrate with a service more down the assembly line is responsible for the correct delivery at the end of the assembly line. Each team should only be responsible for there functional boundary.

This scenario also played at my current project. The project grew and grew and also more teams were added. The integration team had taken the responsibility for testing the whole assembly line and with the growth this responsibility and quality could not be guaranteed. The solution architects then decided to lay the responsibility of testing the services at the team that created them and that a smoke test would be done later on a integration environment. Each team from now on is only responsible for testing the chain of services they create(d) and mock all other services.

The integration SDT took the responsibility to come up with a way to accomplice this. The SDT was giving the following requirements.

  • Functional testing of the boundaries of the domain of a SDT.
  • Implementation should be transparent to all environments, one size fits all.
  • Tooling should be useful for both Unit Testing and Integration Testing.
  • Tooling should be easy to use and should not be unnecessarily time consuming to set up.
  • It should be possible to run tests through a build server (e.g. Hudson)

After researching available tools, SoapUI, WireMock, JAX-WS (Java), MockServer and Moco, showed that MockServer most satisfies the requirements for boundary testing.

What is MockServer

With MockServer (created by James D. Bloom) you can easily mock response messages from each system which you integrate with via HTTP(S).
MockServer supports:

  • Mocking of any HTTP(S) response matched to the received request.
  • Proxing to dynamically route messages.
  • Recording requests and responses to analyse how a system behaves.
  • Verifying which requests and responses have been sent as part of a test.

A few useful scenarios to use MockServer:
Testing:

  • Easy and effective testing of HTTP(S) dependencies (SOAP, REST).
  • Isolating the system / module that needs to be tested (no dependencies).
  • Avoid sharing data between tests that is difficult to manage and maintain and risks tests infecting each other.

De-coupling during development:

  • Start working against a service API before the service is available.
  • Isolating development teams particularly critical during the first phase of development as the APIs / services are highly unstable and Volatile.

The image below shows a traditional system with service dependencies:

Source: http://www.mock-server.com

Figure 1.1 – Source: Website MockServer

To only test the responsibilities (boundaries) the dependencies to Service 1,2 and 3 should be avoided.  By using this MockServer the dependencies and therefore the responsibilities are refuted.

Source: Website MockServer

Figure 1.2 – Source: Website MockServer

Initially you will have to make mock responses for all service calls. This could always be done in a collaboration with other SDTs or deduced on the basis of the message schema that is being used by the service.

MockServer versus SoapUI Mockserver

If you’re considering a specific tool you should also examine how they relate to each other. As described earlier, there are several alternatives. One mayor candidate was SoapUI. SoapUI was already been widely used for unit and integration testing. When unit testing a mockresponse test step was used to return the correct response message and for integration testing the message was sent through the entire assembly line.

With SoapUI it is also possible to set up a mock server (for each service endpoint) which always listens on a fixed endpoint and on the basis of the received request returns one of the configured replies. These mock servers can be deployed on Weblogic as a WAR, so they can also be used for Integration testing.

Advantages:

  • If you have ever used SoapUI you know creating mock responses is a easy task.
  • A SoapUI Mockserver can be deployed as a WAR on multiple environments.

Disadvantages:

  • One Mockserver per service URL or one with multiple service binding under one URL. This also means multiple WAR applications given the URL must be transparent, whether it concerns  a mock of the production system.
  • Dynamically returning correct reponse message is only possible through scripting and this is not an easy task, but rather labor intensive for a typical developer / tester.
  • To return new mock responses they first need to be created in SoapUI and added to existing Mockserver. Which then should be repacked in a new WAR file and redeployed on environments.

Setting up MockServer environment

MockServer can easily be setup on different (types of) environments ans still be transparent. MockServer comes in several flavors namely; standalone JAR (with or without dependencies), deploybare WAR for use on Weblogic, Maven plug-in for starting / stopping server and a Java client API. This blog won’t discuss the latter.

Standalone JAR for local server environment

To use MockServer on your local machine a standalone JAR is available. This comes in two versions, one for Java 1.6/1.7 (Netty) and for Java 1.7 (Jetty). Given a common Weblogic 11g environments is using Java 1.6 we need the version with Netty. The JAR can be downloaded from Maven Central. The version this blog uses is version 2.8.

Ensure that the Java executable is on the environment path, for example, c:\Oracle\Java\jdk16u45\bin. MockServer can be started from the command line using the following command to run:

java -jar mockserver-netty-2.8-jar-with-dependencies.jar -serverPort 8080

The standalone server can also listen on a serverSecurePort (SSL) or start as a proxy using proxyPort parameter.

Deployable WAR for Servlet environment (weblogic)

MockServer is also available als a deployable WAR for has on a servlet environments (Weblogic). If you want to deploy the WAR on Weblogic I suggest to add a weblogic.xml to de WAR with the following content.

The universal WAR can be downloaded from Maven Central. The version this blog uses is version 2.8.

  /mock

    true

To deploy the WAR, go to the WebLogic console of the managed server and login under an administrator account. Click on the link Your Deployed Resources -> Deployments. Open an edit session and install/target the WAR on the managed server. The application will run on the specified context-root in weblogic.xml (e.g. /mock).

Setup end-to-end test situation

As indicated in the previous illustration (figure 1.2) dependencies to services, outside the SDTs domain, should be avoided. At first you will have to set mock responses (expectations) for each service-call that is send to a service outside its domain. In this blog we will look at the basics setting up, cleaning up and checking expectations using SoapUI. To

Available REST APIs

MockServer publishes a number of REST APIs, which can affect the operation and result. To give you a head start I created a WADL and SoapUI Project Template.

ServiceResource URIHTTP MethodDescription
Set Expectation/expectationPUTSet expectation that is returned if the incoming request message matches the request object of the expectation.
Clear Expectation(s)/clearPUTClear, based on the given request object, the corresponding expectations that were set.
Reset/resetPUTReset all expectation which were set.
Verify/Retrieve/retrievePUTReturns, based on the given request object, a list of the expectations that have been returned by MockServer.
Dump to Log/dumpToLogPUTDump, based on the given request object, the corresponding expectations to a log file.

In the next part we will touch the basics of setting, clearing and verifying expectations.

Setting an expectation

Setting, verifying and deleting expectations are done by sending a JSON (JavaScript Object Notation) object to the REST interface of MockServer.  The JSON message to set an expectation contains a httpRequest and httpResponse object, httpRequest describes the values ​​on which a received message is to be matched and httpResponse describes what answer should be returned at that time.

The httpRequest Object

{
    "httpRequest": {
        "method": "POST",
        "url": "http://host:port/services/MessageService/1.0",
        "path": "/services/MessageService/1.0",
        "queryStringParameters": [
            {
                "name": "paramName",
                "values": ["paramValue1", "paramValue2"]
            }
        ],
        "cookies": [
            {
                "name": "ecId",
                "values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
            }
        ],
        "headers": [
            {
                "name": "Content-Type",
                "values": [".*action=\".*/name-of-operation\".*"]
            }
        ],
	  "body": {
            "type": "XPATH"
            "value": "//IdentificationCode='MSG03491'"
        }
    },
    "httpResponse": { ... }
    "times": { ... }
}
Element NamePossible valuesDescription
methodGET, POST, PUT, etchttp method of incoming request
urlExact match or regexURL where MockServer listens on
pathExact match or regexPath from root where MockServer listens on
queryStringParametersName/Values pairsMatch on query parameters in url/path
cookiesName/Values pairsMatch on cookie parameters (http header)
headersName/Values pairsMatch on header parameters (http header)
bodyType/Value pair
Type = XPATH, EXACT, REGEX, PARAMETERS
Match on the body content. Using XPATH a fully qualified namespace is needed, a prefix does not work. When using the Venetian blind model you can use a wildcard = //elementname=’value’
The httpResponse object
{
    "httpRequest": { ... },
    "httpResponse": { 
        "statusCode": 200,
        "cookies": [
            {
                "name": "ecId",
                "values": ["2By8LOhBmaW5nZXJwcmludCIlMDAzMW"]
            }
        ],
        "headers": [
            {
                "name": "Content-Type",
                "values": ["application/soap+xml; charset=utf-8"]
            }
        ],        
        "delay": {
            "timeUnit": "SECONDS",
            "value": 1
        }        
        "body": ""
    }
    "times": {
        "remainingTimes": 1,
        "unlimited": false
    }
}
Element NamePossible valuesDescription
statusCode200, 301, 404, 501, etchttp statuscode of outgoing response
cookiesName/Values pairsReturning cookie parameters
headersName/Values pairsReturning header parameters
delayTimeUnit/value pair
timeUnit = SECONDS, MINUTES, HOURS, etc
Delay before returning response
bodyContent of responseNotice: text on 1 rowMatch on the body content. Using XPATH a fully qualified namespace is needed, a prefix does not work. When using the Venetian blind model you can use a wildcard = //elementname=’value’
The times object
Element NamePossible valuesDescription
remainingTimesNumeric valueThe number of times that the expectation can be returned
unlimitedBoolean (true / false)Hold expectation till reset is executed

Service WSDL expectation

At the time that a component determines the endpoint based of a WSDL, such as SOA Suite, then these WSDL should also be served by MockServer. A useful tip when using MockServer in combination with SoapUI is the use of variables in the body text of the WSDL. Let the XSD import point to the existing path, but point the endpoint to the path where MockServer listens on.

Imagine parameter varEndpoint is set to http://host:port/services/MessageService/1.0 and parameter varEndpointMock is set to http://host:port/mock/services/MessageService/1.0.

Below the JSON message to set a WSDL expectation:

{
    "httpRequest": {
        "method": "GET",
        "path": "/services/MessageService/1.0",
        "queryStringParameters": [
            {
                "name": "wsdl",
                "values": [""]
            }
        ]       
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": [
            {
                "name": "Content-Type",
                "values": ["text/xml; charset=utf-8"]
            }
        ],
        "body": ""
    },
    "times": {
        "unlimited": true
    }
}

A WSDL is requested on the basis of the GET method. In order to keep it environment independent we make use of the element path instead of the element url. The path element specifies the path where MockServer needs to listen to, and usually equals the endpoint of the service. In addition, it is important that a WSDL is returned only if the query string ?wsdl is given. This can be checked by by adding a queryStringParamters name/values pair to the httpRequest object of the expectation. With the name wsdl and an empty value.

The httpResponse has a valid statuscode “200 (OK)”. To ensure that browsers, but also applications understand that it is a XML document, the expectation returns in addition to the XML response (body) a header parameter Content-Type with value “text/xml; charset=utf-8”, where the charset specifies the character set of the body.

SOAP call expectation

Setting a SOAP call expectation is similar, but contains some crucial differences compared with a WSDL expectation. SOAP calls use the POST method. The path element is the same as the WSDL expectation, but it does not use any query parameters.

Below the JSON message to set a SOAP call expectation:

{
    "httpRequest": {
        "method": "POST",
        "path": "/services/MessageService/1.0",
        "headers": [
            {
                "name": "Content-Type",
                "values": [".*action=\".*/name-of-operation\".*"]
            }
        ],
        "body": {
            "type": "XPATH",
            "value": "//xpath-to-element='value'"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": [
            {
                "name": "Content-Type",
                "values": ["application/soap+xml; charset=utf-8"]
            }
        ],
        "body": ""
    }
}

What is important to specify for this type of expectation is the SOAP action for which the message arrives. A WSDL serves one or more operations, wherein the operation name is usually part of the SOAP action. The SOAP action is part of the header parameter Content-Type , wherein the key/value action used. Regex may be used for the extraction of the value.

The httpRequest object can also make use of body matching. This allows the contents of received messages to be compared with the expected request message for this expectation before returning it. When mocking SOAP/XML services the value XPATH can be used for the element type and an XPath expression for the element value to check the contents.

Notice:  When using namespaces a prefix is not enough, it should be a fully Qualified name. When using the venetian blind model (unqualified xsd elements) you can use a wildcard = //elementname=’value’.

The httpResponse has a valid statuscode “200 (OK)”. To ensure that Service Bus and SOA Suite understand that it is a SOAP message, the expectation returns in addition to the SOAP/XML response (body) a header parameter Content-Type with value application/soap+xml; charset=utf-8″, where the charset specifies the character set of the body.

Fault expectation

When using MockServer, it is possible to test the most obscure errors. Errors which are usually difficult to test, because something physically needs to be shutdown. Using MockServer this not only applies to services but for all communications over HTTP(S). Think of authorization e.g. kerberos. It is possible to return any arbitrary status code. For a SOAP fault status code 500 is used, but for a service not found the code 404 is used. Below is a list of the most well-known status codes.

StatuscodeOmschrijving
401 UnauthorizedThe page you are trying to reach can not be loaded until you first log in with a valid username and password.
403 ForbiddenAccess to the page or resource that you tried to reach is prohibited.
404 Not FoundThe URL you are trying to reach could not be found.
408 Request TimeoutThe request sent to the server has received a timeout.
500 Internal Server ErrorA very general HTTP status code means that something has gone on the server wrong. This status code is used by SOAP faults.
501 Not ImplementedThe server does not support the functionality that is needed in order to satisfy the request. The server does not recognize / support the request method used.
503 Service UnavailableThe server is simply not available at the moment.
Below the JSON message to set a SOAP fault expectation:
{
    "httpRequest": {
        "method": "POST",
        "path": "/services/MessageService/1.0",
        "body": {
            "type": "XPATH",
            "value": "//xpath-to-element='value'"
        },
        "headers": [
            {
                "name": "Content-Type",
                "values": [".*action=\".*/name-of-operation\".*"]
            }
        ]        
    },
    "httpResponse": {
        "statusCode": 500,
        "headers": [
            {
                "name": "Content-Type",
                "values": ["application/soap+xml; charset=utf-8"]
            }
        ],
        "body": """
    }
}

Verifying accessed expectations

It may happen that there is a decoupling point in the assembly line, and you won’t know if an expectation is accessed/returned. A possible decoupling point is a queue or an one-way call. If so you can verify if a expectation is accessed by calling the retrieve API. With the retrieve API you can verify if a expectation was actually accessed. You can retrieve accessed expectations on one or more fields within the httpRequest object. In any combination you prefer. An empty JSON object returns all accessed expectations.

Below the JSON message to retrieve all accessed WSDL expectations:

{
    "method": "GET",
    "queryStringParameters": [
        {
            "name": "wsdl",
            "values": [""]
        }
    ]
}
Or a specific SOAP call expectation:
{
    "method": "POST",
    "path": "/services/MessageService/1.0",
    "body": {
            "type": "XPATH",
            "value": "//IdentificationCode='MSG03491'"
        },
    "headers": [
        {
            "name": "Content-Type",
            "values": [".*action=\".*/retrieveMessage\".*"]
        }
    ]      
}

A single field within the httpRequest object is sufficient to find matches. Each additional field refines the result.

If SoapUI is used to send a test request to MockServer to retrieve accessed expectations a Script Assertion can be used to check if the result contains the expected messages. In combination with JsonSlurper object in Groovy the JSON response message can be converted to a DOM object.

MockServer_retrieve_assertion

Clearing Expectations

It is possible that a testsuite fails on a intermediate teststep. To be sure that there no expectations remain in the memory of MockServer it is wise to clean the expectation that where set for this testsuite.

To test with a clean slate the REST resource /reset can be used to clear all expectation that where previously set. By sending an empty JSON object to this resourse, MockServer will remove all expectations.

At the moment a teststep fails in the middle of a testcase, it is wise to clean alle expectation set by the testcase before the next testcase starts. MockServer holds all the expectations firmly until they are requested/accessed and uses a first-in-first-out system to return matched expectations. Also, it is useful to always clear expectations set in the running testcase after a successful run., because the /retrieve resource retains the data about accessed expectations. If you would run the same testcase again the returned data for the retrieve/verify teststep could give a different result then expected.

Expectations can be erased by sending a JSON message with matching httpRequest to the REST resource /clear.
Below a JSON message to clear all expectations that match the retrieveMessage operation of the MessageService.

{
     "method": "POST",
     "path": "/services/MessageService/1.0",
     "headers": [
         {
             "name": "Content-Type",
             "values": [".*action=\".*/retrieveMessage\".*"]
         }
     ]
}

A single field within the httpRequest object is sufficient to clear matches. Each additional field defines the result.

What is next?

The blog gave a in-depth coverage of the basic use of MockServer in a service-based environment. It answered questions as what MockServer is, how you can use it and it gave some examples. In an upcoming blog I will present a full use case based on multiple services and service components (SB & SCA).

To start using MockServer download the JAR or WAR file and use the WADL (Web Application Description Language) or the template project for SoapUI (download).


About Author

Robert is a integration developer at heart and one of the expertise leads on Integration, Blockchain and IoT at AMIS. He is an respected author, speaker at (international) conferences and is a frequent blogger on the AMIS Technology blog, the Oracle Technology Network, and participates in OTN ArchBeat Podcasts. Robert is an member of the board of the Dutch Oracle User Group (nlOUG) and also organizes meetups.In 2017, Robert was named Oracle Developer Champion, but also hold the Oracle ACE title, for his contributions to the community. He is co-author of the first Oracle PaaS book published, which was published in January 2017. His fascination for technology had led to the research of Blockchain and is currently writing a book called Blockchain across Oracle.

Comments are closed.