fb
Uncategorized 10 minutes

Black box testing complex ESB services

Thijs Volders
Thijs Volders
Strategic Technology Officer
black box testing esb
Scroll

black box testing complex esb servicesTesting is perhaps one of the most underrated tasks of software development. Yet, we really can’t do without testing. We already wrote some blogs about testing and now we are going to look into black box testing complex ESB services. We will use SoapUI, a tool that has more to offer than only simply sending out messages. 

Simple services, simple tests

Some ESB services are simple straight forward services: An incoming request is transformed into a request the back-end service understands, and its response is transformed into a respond the caller understands (see figure 1).

Such a simple service can easily be tested by using a stub instead of the real back-end together with a tool like SoapUI. In a SoapUI test case:

  • The SoapUI test runner sends a request to the ESB (1)
  • The ESB transforms the request
  • The ESB sends the transformed request to the back-end stub (2)
  • The back-end stub returns a predefined response (3)
  • The response is transformed in the ESB
  • The ESB returns the transformed response to the SoapUI test runner (4)
  • The test runner executes some assertions to make sure the ESB service returns what we expect

ESB requests.png

Figure 1

Complex services, hard to test

In reality, not all services are as easy as the one described above. Not all processes in the black box we call back-end are synchronous processes, a lot of processes are asynchronous: processes that involve a human decision, a process may not continue until a payment is received, or … well use your own imagination here.

To decouple systems, we might introduce a queue into an integration service. The service running on the ESB still allows consumers of the service to send requests even when the back-end service is down or unreachable. The requests will be kept in the queue and will be processed when the back-end system is up again and available. Only when the message is rejected by the back-end service, for instance because of validation rules, the message will be sent to a Dead Letter Queue. (A common implementation of EIP Dead Letter Channel.) 

Dead letter channel-1.png

 Figure 2 Dead Letter Channel

Let’s have a look at a typical complex service as shown in figure3:

  • A client (in this picture the SoapUI client) calls the service (1)
  • The service puts the message in a queue for further processing (2)
  • A response is sent to the caller (3)

When we use SoapUI to test this service and we don’t do anything special, all we can test now in the testcase running in the SoapUI test-runner, is the delivery of the message to the queue. Is that what we really want to test in this integration? No, it’s not! 

Let’s have a look at the rest of the integration:

  • Some component is listening to the queue and when a message is present, the message is retrieved from the queue (4) and a complex service is invoked.
    This service:
    • Calls back-end service 1 (a stub during our tests) (5)
    • Calls back-end service 2 (a stub during our tests) (6)
    • Answers of both back-end service 1 and back-end service 2 are combined and transformed into a new message
    • This new message is sent to back-end service 3 (a stub during our tests) (7)

SoapUI integration.pngFigure 3

I would like to know what is sent to back-end 3. Depending on the complexity of the transformations responsible for the creation of the requests sent to back-end 1 and back-end 2 we might want to know what is sent to these services too.

(When the messages sent to back-end 1 and back-end 2 are simple and the responses of these service are testable by inspecting the request sent to back-end 3 we might decide to skip the inspection of the requests sent to back-end service 1 and back-end service 2.)

I’ll focus on testing the request sent to back-end 3 now, but the technique I’ll use to test this request can be used to test the requests sent to back-end 1 and back-end 2 too.

Groovy script and properties

SoapUI offers you the tools to make your stubs intelligent, you can use groovy script to return different responses when different requests are sent. This feature enables you to make your stubs as realistic as you want:

  • Return an OK message when a correct request is sent
  • Return a fault message when an invalid request is sent.

In groovy script this looks like:

if( conditionIsMet ) {
   return "OkResponse"

} else {
    return "ErrorResponse"
}

SoapUI properties

SoapUI also has a concept called properties, instead of hard coding the location of the service (ESB) you are about to test, you can use placeholders in the URL’s of the requests.

These placeholders will be replaced by the value of properties defined in your SoapUI test project. These properties can be assigned their values when the test runner is started, so you can create tests that can be executed on several servers (ESB instances) which is wonderful.

Normally you will specify the SOAP (or REST) endpoint like http://localhost:8080/blackboxSOAP   

When you use placeholders, you will specify this endpoint like http://${ #Project#mockURL}/blackboxSOAP

In the latter case, the mockURL property in the Project context will be used to determine the endpoint URL. This property makes it possible to run the same test script in different environments (dev, test, acc, prod).

These properties can also be accessed when the stubs are exported as a war and are run on a servlet container like Tomcat, Jetty, WSO2AS, …

Script and properties

In SoapUI it is also possible to access the properties from the groovy scripts that can be run when a request is received by a stub. The properties can be both be read from and written to, that’s exactly what I use for my black box tests.

Complex services, no longer hard to test

Let’s have a look at figure 4:

Figure 4 complex services.png

Figure 4

This picture is in fact figure 3 with only a few items added:

  • When back-end service 3 is invoked, before returning a response, a groovy script is run on the stub. This script stores the complete received request in a property on the stub (8)
  • In the test runner, after the original response is received (3), we wait for a few seconds, this period must be long enough for the rest of the process to finish.
  • An extra REST service on the stub is invoked, this REST service returns the property which is filled earlier when the back-end stub was processing its service call. (9)
  • And now, in the test runner we can execute assertions against the request sent to back-end 3. Isn’t it wonderful?

The weak part in this scenario is the delay, an even nicer approach would include invoking the REST service until a result is received. And of course, one must include some timeout, otherwise a failing test might keep polling the REST service forever. 

The gory details

Let’s have a look at the gory details, I promise you it’s no rocket science.

Storing the request as a property

Configure the stub to dispatch a groovy script. This very short script stores the request in a property in the project scope. And lets the stub return a response identified by its name, in this case ‘MessageName’

The store request script:

Def project = mockOperation.mockService.projectProject.setPropertyValue("Request", mockRequest.requestContent) Return "MessageName"

Retrieving the stored request

Define an extra REST (GET) resource in your stub and let it dispatch a groovy script as well. Sounds familiar, doesn’t it? Only this script doesn’t store a request in a property, no this one returns all properties in the project scope in a single JSON response.

The get the properties script (REST API):

import groovy.json.* 

def project = mockOperation.mockService.project
def kvs = [:] 

project.properties.each
 kvs[it.key] = project.getPropertyValue(it.key)


def
builder = new JsonBuilder(kvs)
context.content = builder.toString()
return "TheResponse"

In the last line before the return statement, the JSON message is assigned to the variable content which lives in the message context. The body of the response that is returned by the groovy script (a response called ‘TheResponse’) contains only one line: 

${content} 

Erasing a stored request

It is not uncommon to test and invoke one backend stub multiple times in one test run, after all we don’t want to test the happy flow only, we want to test a variety of exceptions too.

To make sure we start our test with a clean ‘request property’, it makes sense to erase the property before a new request will be sent to the back-end stub.

Define an extra REST (DELETE) resource in your stub and let it dispatch a groovy script as well. Sounds familiar, doesn’t it? Only this script doesn’t store a request in a property, no this one returns all properties in the project scope in a single JSON response.

The erase a property script (REST API):

import groovy.json.* 

def requestPath = mockRequest.getPath()
def propToDelete = requestPath.substring(29)
def project = mockOperation.mockService.project
def count = 0 

project.properties.each if(it.key.equals(propToDelete)) {         project.removeProperty(it.key)   
 count++ 
 }
} 
def response=[nrOfDeletedProperties: count]
def builder = new JsonBuilder(response)
context.content = builder.toString()
return "DeleteResponse" 

In this script, we:

  • Determine the name of the property to delete
    (This particular script was called with the URL http://host:port/bbMockProperties/properties/{property}
    The context path before the {property} is exactly 29 characters substring 31 in the script.)
  • The script iterates over all the properties in the project context and checks if the name of the property matches, if it does the property is removed and a counter is increased.
  • A JSON result is assigned to the ‘content’ property in the response context.
  • Finally the response named ‘DeleteResponse’ is returned. This message again contains only one line:
  • ${content}

Executing the assertions

In SoapUI you can define assertions to check if the response received after a service invocation matches your expectations. Multiple types of Assertions are supported and for the verification of the message sent that was sent to the back-end service we use a ‘Script Assertion’ … which uses groovy too.

The Assertion script:

import groovy.json.JsonSlurper 

// Process the JSON message
def ResponseMessage = messageExchange.response.responseContent
def jsonSlurper = new JsonSlurper().parseText(ResponseMessage)
assert !(jsonSlurper.isEmpty()) 

// Process the SOAP request
def content = jsonSlurper.bbRequest
log.info content
def holder = new com.eviware.soapui.support.XmlHolder( content ) 

holder.namespaces["bb"] = "http://blog.yenlo.com/blackbox" 
assert("12345".equals(holder["//bb:Request/bb:OrderId"]))assert("BlackBox".equals(holder["//bb:Request/ns:Item"]))assert("5".equals(holder["//bb:Request/bb:Quentity"]))

In this script:

  • The request is received as a part of a JSON message, so we process it as a JSON message first. We use the JsonSlurper class to does this for us, which gives us a way to easily retrieve the values of the properties that are retrieved from the stub.
  • In this particular example, we retrieved a SOAP message as a JSON property. So we have to use another helper, the XmlHolder, to process the SOAP/XML message.
  • Before we can let the XmlHolder execute xpath expressions, we have to declare the namespaces we are about to use in the xpath expressions.
  • Finally we can execute our assertions like assert("12345".equals(holder["//bb:Request/bb:OrderId"]))
    here we check if in the SOAP request the field …/OrderId has the value “12345”
  • If any of the script assertions fails, the whole test in SoapUI fails. 

Resume

By using/abusing the scripting capabilities of SoapUI we can extend stubs giving us the possibility to check the message sent to these stubs. By checking if the messages sent to these stubs are as expected, given the known message sent to our service under test, we are able to perform black box testing on our services.

If you have any questions about this blogpost contact us via the comments section of this blog. View also our WSO2 Tutorials, webinars or white papers for more technical information. Need support? We do deliver WSO2 Product SupportWSO2 Development SupportWSO2 Operational Support and WSO2 Training Programs.

{{cta(‘d96cddee-168c-42a5-a0b6-00a670e766b7’)}}