Popular Posts

Friday, January 27, 2012

SOAP message correlation with JMeter Beanshell pre-processor

When testing service oriented solutions, it is often required to correlate SOAP or POX messages with each other. For example, first you may need to talk to one particular web service and get the response back. Then you will need to extract some properties from the response and include them in subsequent requests. In these situations, you cannot just send the SOAP messages to the web service. In other words, you should do some preprocessing before doing the second service call.

Apache Jmeter provides you with Pre-Processor elements to handle these types of requirements. In this post, we will look into one of the useful pre-processor element, Bean Shell PreProcessor

We will do a simple echo web service call first and extract the response value to a user defined variable. Then, we will use Bean Shell PreProcessor to modify the second SOAP request.

Step 1
Start Jmeter and create a new test plan. Add two SOAP/XML-RPC request samplers. Add the following request for one sampler. Name the sampler as "echoStringRequest"

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.carbon.wso2.org">
<soapenv:Body>
<ser:echoString>
<ser:s>MSFT</ser:s>
</ser:echoString>
</soapenv:Body>
</soapenv:Envelope>

For the second SOAP/XML-RPC request sampler, add the following request. Name it as "StockQuoteRequest"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<p:getQuote xmlns:p="http://services.samples/xsd">
<p:request>
<p:symbol>&lt/p:symbol>
</p:request>
</p:getQuote>
</s:Body>
</s:Envelope>

Note that there is no relationship between these two web service calls. I use these just for the demonstration purposes only.

Step 2:

The above two requests will be sent to two web services hosted in Apache Axis2 (or WSO2 Application Server). Therefore, download Axis2Service.aar and SimpleStockQuoteService.aar deploy the services.

Step 3:

Now, Send the above echoStringRequest to Axis2Service and check the response. You will see something like below.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:echoStringResponse xmlns:ns="http://service.carbon.wso2.org"><ns:return>MSFT</ns:return>
</ns:echoStringResponse>
</soapenv:Body>
</soapenv:Envelope>


We are going to extract the value of <echoStringResponse> element and assign it to a variable so that we can use the value later. For that, we need to add the User Defined Variables config element to the thread group. Right click on thread group and select Add --> Config Elements ---> User Defined Variables

Name: echoResponse
Value: empty



Step 4:

We need to extract the echoStringResponse from the above SOAP response. For that, we will use XPAth extractor post-processor element.

Right click on the echoStringRequest SOAP/XML-RPC sampler and add XPath Extractor.



Specify "//echoStringResponse/return" as the Xpath query and "echoResponse" (the user defined variable) as the reference name.

Step 5

In the previous two steps, we extracted echoStringResponse value from the SOAP response message and assign it to a user defined variable. Now, we need to insert the extracted string into the second SOAP request.
As I mentioned at the beginning, we use BeanShell preprocessor to modify the SOAP request before submission.

BeanShell is a simple scripting language which dynamically executes standard java syntax.

Lets add the beanshell preprocessor as a child of our second SOAP sampler, StockQuoteRequest.
Right click on StockQuoteRequest SOAP/XML-RPC sampler and select Add --> Pre Processors --> BeanShell Pre Processor

You can include the processing script inside script pane. Here, first we read the StockQuoteRequest to a String and replace the <symbol> element with the echoResponse value which has been placed under the user defined variable.

import org.apache.jmeter.protocol.http.sampler.SoapSampler;
SoapSampler soapSampler = (SoapSampler) sampler;
String stockRequest = soapSampler.getXmlData().replaceFirst("#symbol#", vars.get("echoResponse"));
soapSampler.setXmlData(stockRequest);
SoapSampler class can be used to manipulate a lot of operations of SOAP/XML-RPC request such as read XML data, modify data and set SOAPAction etc..

Here, sampler is a variable provided by jmeter which refers to the parent SOAP request.



Step 6

We have completed adding all the necessary elements to our Jmeter test plan. Now, add a listener to visualize the results and run the test.

You will see that the response of the echoStringRequest will be used for the StockQuoteRequest by extracting the echoStringResponse value from the first request.

Thursday, January 26, 2012

Testing Perspective of API management

API(Application programming interface) is not new, it has been a commonly used term in computing for decades. But what is management of APIs?

When a set of entities become larger and grow in exponential manner, there is alway a need of management. You can have your own shop and you are able to look after each and every aspect of its operations till it grows to some level. Once, you have a chain of shops, you, by your own cannot do the same which you did with one shop. You should have some kind of management to govern and control your business operations.
Similarly, management is a common requirement for most of the entities around us. DBMS systems were built to manage pools of data.
If you expose the computing assets of your organization to external parties so that they can build applications to integrate with your systems, it can be considered as an API offering. With the widespread adoption of mobile devices and service oriented computing, business organizations began to open application and services to external developers. These third party developers built applications to integrate with the systems of API provider allowing the provider to extend the capabilities of their business as well as helping the third party developers to earn by their own.
Today, there is a enormous set of applications build by third party developers based on the APIs exposed by large vendors. For example, how many new android, iPad, iPhone apps out there for download? how many facebook, twitter apps?

With this demand of APIs, there is a necessity of adopting a proper API management if you are a Internet application vendor who expose the business APIs for third party developers. Similar to database management software, API management software is used primarily to help API publishers to expose the APIs and third party developers to build applications in simple and efficient manner. Mashery is considered as the first known API management solution. An API management software should be capable of governing and controlling access to the APIs, securing APIs, metering and monitoring usage of APIs etc..

With large amount of APIs offered by many vendors as well as the adoption of API management solutions, should we expect a radical change in software testing space? Should there be a totally different approach for API testing?

Since there is a close relationship with services in service oriented architecture (SOA) and web APIs, I believe the approach which I suggested for SOA testing will still be applicable for APIs.
Similar to web services, the APIs which are exposed to outside, for various application development and integration efforts, must be tested end-to-end to verify both functional and non-functional needs. In API development, everyone (both QA and Development) should equally be responsible for quality. You should not even think of exposing your APIs for public use if you do not have automated tests to verify regressions. Think about the performance impacts, think about security concerns, think about usage patterns of your APIs and derive a comprehensive test plan.

Open source tools such as soapUI which we use for web services testing can also be used for API testing out of the box. Specially, soapUI can easily be used for REST API testing.

I do not think there will be a completely different set of tools for API testing since API testing has been there for years and most of the tools as well as the constructs provided by programming languages such as Junit, Nunit etc.. have been used in API testing in all these years.

However, I believe, the API management vendors should think more about the testing and quality assurance aspects. Quality is as important as the govern, control, monitoring aspects of web APIs. Therefore, it will be important for API management solutions, at least to include a set of tools for testing the APIs managed by them.

Saturday, January 21, 2012

Truly RESTful Services using Apache Wink and WSO2 Application Server





Apache Wink is a complete implementation of JAX-RS v-1.1 specification. JAX-RS is a java API which supports creating web services in RESTful manner.
WSO2 Application Server is the enterprise-ready, scalable web services and web app hosting platform powered by Apache Axis2 and Apache Tomcat.
In this post, we are going to create a RESTful web service using Apache wink libraries and deploy it in WSO2 Application Server. If we summarize the steps that we are going to do in this example;
1. Create a JDBC data source (mySQL)
2. Create the web service with JAX-RS annotations which makes use of the above database
3. Create a web application (war) with apache wink libraries
4. Deploy the web app in WSO2 Application Server

WSO2 provides application developers with a complete middleware platform in cloud. Therefore, we makes use of WSO2 StratosLive PaaS (Platform as a Service) as our development platform. In other words, we are NOT even going to install mySQL or WSO2 Application server in our machines to try this sample out. We will create the database in cloud and host web application in the cloud.

Without discussing further. lets start our journey.

Step 1

We are going to create a primitive customer registration application which consists of a mySQL database with one table called customer. We need to create the DB schema.
WSO2 Stratos middleware platform in cloud allows us to have our own data storage in cloud within seconds. As I explained in this post, all we have to do is;
- Register a new tenant
- Log into https://stratoslive.wso2.com
- Access DataServices Server
- Create a database and a table

CREATE TABLE CUSTOMER_T(customerID int, customerName varchar(100), customerAge int, customerAddress varchar(200));

Once the database and table is created, note the DB connection URL. In my case, it is,
jdbc:mysql://rss1.stratoslive.wso2.com/wink_superqa_com

Step 2

Now, our application data source is ready for use. As I have explained before, we are going to create a customer registration web service which includes all CRUD operations associated with above DB schema. In other words, we can add, delete, read and update customers using the web service. We will implement our webservice in completely RESTful manner so that we makes use of HTTP verbs to invoke service.
Therefore, lets create the Customer bean first. Open your favorite Java IDE and add Customer class with the following properties and associated getters and setters. The complete class can be found at https://wso2.org/repos/wso2/trunk/commons/qa/qa-artifacts/app-server/rest/jaxrs-sample/src/com/beans/Customer.java

public class Customer {

private int customerID;
private String customerName;
private String customerAddress;
private int customerAge;


Step 3

We will use a separate class to handle all communication with the database which we created above. Lets name the class as Storage.java and implement all CRUD operations.

public class Storage {


Connection connection = null;
Statement statement = null;
ResultSet rs = null;

private Connection getConnection() {
String driverName = "com.mysql.jdbc.Driver";
String conectionURI = "jdbc:mysql://rss1.stratoslive.wso2.com/wink_superqa_com";
String userName = "ck_l96225fe";
String password = "ck";


try {
Class.forName(driverName);
try {
connection = DriverManager.getConnection(conectionURI, userName, password);
} catch (SQLException e) {
e.printStackTrace();
}
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}

} catch (ClassNotFoundException e) {
e.printStackTrace();
}

return connection;
}

//CREATE operation
public void addCustomer(Customer customer) {


try {
connection = getConnection();
statement = connection.createStatement();

String sqlStatement = "INSERT INTO CUSTOMER_T VALUES (" + customer.getCustomerID()
+ ",'" + customer.getCustomerName() + "', " + customer.getCustomerAge() + ",'" + customer.getCustomerAddress() + "')";
statement.execute(sqlStatement);

} catch (SQLException e) {

} finally {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

if (connection != null) {
try {
connection.close();
} catch (SQLException e) {

e.printStackTrace();
}
}
}


}

Similarly, add the other methods to UPDATE, DELETE and READ operations. The complete Storage.java class can be found here.

Step 4

Now, we can implement our web service. We are going to use JAX-RS annotations to make our web service completely RESTful. Download Apache wink distribution from here and add WINK_HOME/dist and WINK_HOME/lib to class path of your IDE.
We will have four methods to demonstrate the basic HTTP methods.

  • addCustomer() method is used to add a new customer to the system. We use a HTTP POST request to send the customer details to the web service method.
  • getCustomerName() is just a HTTP GET operation which reads the CUSTOMER_T table and send back the name of the customer associated with given customer ID.
  • updateCustomer() method updates the customer address of the given customer
  • deleteCustomer() method removes a customer record from the table
In JAX-RS, we can define the root resource with @Path annotation as shown below. So, when you send a HTTP request to /qa, it will be directed to the associated class.

@Path("/qa")

public class CustomerService {}

Now, we need to implement the methods associated with CRUD operations. First lets look at addCustomer method. As we defined the root resource at the class declaration level, we can define subresource methods to handle the common HTTP methods. Here, addCustomer is a subresource method and we annotate it with @POST to direct POST requests which are targeted to '/qa' root resource. @Path annotation at the sub resource level resolves the URL path which is targeted to the method. In otherwords, if the request URL is, /qa/customer and the HTTP method is POST, the request is dispatched to addCustomer() method.

@POST

@Consumes("application/x-www-form-urlencoded")
@Path("/customer")
public void addCustomer(@FormParam("customerid") int customerID, @FormParam("customername") String customerName, @FormParam("customerage") int customerAge, @FormParam("customeraddress") String customerAddress){

Storage storage = new Storage();
Customer customer = new Customer();
customer.setCustomerID(customerID);
customer.setCustomerName(customerName);
customer.setCustomerAge(customerAge);
customer.setCustomerAddress(customerAddress);
storage.addCustomer(customer);


}


Also, make a note of the @Consumes annotation, here we define the Content-Type which must be included in the HTTP POST request. In our example, if a POST request to '/qa/customer' is issued with "application/x-www-form-urlencoded" Content-Type, the addCustomer() method will be invoked. If we send any other content in the POST request, this method will not get invoked.

We also use annotated parameters to pass some additional information with a request. For example we can use query parameters, path parameters etc and process them accordingly. In our example, we use @FormParm parameter to extract parameter values from the form posts.

Similarly, we can implement the other methods in our service implementation class.

@GET

@Path("/customer/{customerid}")
@Produces("text/plain")
public String getCustomerName(@PathParam("customerid") int customerID) {
Storage storage = new Storage();
Customer customer = null;
try {
customer = storage.getCustomerDetails(customerID);
} catch (SQLException e) {
e.printStackTrace();
}
return customer.getCustomerName();
}


@PUT
@Consumes("application/x-www-form-urlencoded")
@Path("/customer")
public void updateCustomer(@FormParam("customername") String customerName, @FormParam("customeraddress") String customerAddress){

Storage storage = new Storage();
storage.updateCustomer(customerName, customerAddress);


}

@DELETE
@Path("/customer/{customerid}")

public void deleteUser(@PathParam("customerid") int customerID) {

Storage storage = new Storage();
storage.deleteCustomer(customerID);

}


Step 5

We have completed the implementation of our web service class with JAX-RS annotations. Apache wink needs us to create a sub-class of javax.ws.rs.core.Application if we deploy deploy our application on non-JAX-RS aware containers. At the time of writing, WSO2 Application Server is not JAX-RS aware hence we need to create this particular subclass. This class basically returns the root resource(s).

public class CustomerResourceApplication extends Application {


@Override
public Set> getClasses() {
Set> classes = new HashSet>();
classes.add(CustomerService.class);

return classes;

}
}


Step 6

Next, we need to create the web.xml file for our web application. In addition to the standard constructs in web.xml, we should define that the Apache wink JAX-RS servlet should be initialized with an instance of the above CustomerResourceApplication.

We also define that the requests begin with '/rest/' will be handled by Apache Wink JAX-RS servlet.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Restful service Test Web Application</display-name>
<servlet>
<servlet-name>CustomerServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sample.CustomerResourceApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CustomerServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>


Step 7

We are done with our web application now. Create a war with the above classes as well as apache wink libraries. Make sure to place all jars included in WINK_HOME/dist and WINK_HOME/lib in WEB-INF/lib of the web application. You can use the ant build script given here to do all these stuff.

Step 8

Once the web application (CustomerService.war) is ready, log in to https://appserver.stratoslive.wso2.com with your tenant credentials and upload the web application. (Please read my blog post on Apache Tomcat As a Service if you want to know how WSO2 application Server can be used in web app deployment)

Step 9

Finally, we can invoke each of the operations of our web service in truly RESTful manner using a client application such as curl.

HTTP POST:
curl --data "customerid=1&amp;customername=charitha&amp;customerage=33&amp;customeraddress=piliyandala" -X POST -H "Content-Type: application/x-www-form-urlencoded"http://appserver.stratoslive.wso2.com/t/superqa.com/webapps/CustomerService/rest/qa/customer



HTTP GET:
curl -X GET http://appserver.stratoslive.wso2.com/t/superqa.com/webapps/CustomerService/rest/qa/customer/1



HTTP PUT:
curl --data "customername=charitha&amp;customeraddress=colombo" -X PUT -H "Content-Type: application/x-www-form-urlencoded" http://appserver.stratoslive.wso2.com/t/superqa.com/webapps/CustomerService/rest/qa/customer


HTTP DELETE:
curl -X DELETE http://appserver.stratoslive.wso2.com/t/superqa.com/webapps/CustomerService/rest/qa/customer/1

SOAP with HTTP basic auth using Apache JMeter

SOAP/XML-RPC request sampler of Apache Jmeter can be used to send SOAP requests to a web service. We looked into the details of SOAP/XML-RPC sampler in a previous blog post.
If the web service is secured, we cannot directly send messages using the above sampler. This post will help you to use Jmeter in web service testing if the service is secured using HTTP basic authorization.

If a web service is secured using HTTP basic authorization, the authorization credentials are carried over HTTP headers of the message. The security information is not coupled with the SOAP envelope. Therefore, the same procedure which we are going to discuss below can be applied to any other sampler in Jmeter.

Step 1:

Have a web service secured with HTTP basic authentication. I use Apache Axis2 as the web service container and deploy it on Apache Tomcat. Then use the tomcat authorization to secure any service hosted in Axis2 As explained by Prabath in here.
If the service is secured with HTTP basic auth, the service can only be invoked if you send the request with Authorization header as follows.

Authorization: Basic Y2hhcml0aGE6Y2hhcml0aGE=

Step 2:

We need to insert this header into SOAP messages which transmits over HTTP channel. In other words, we need Jmeter to add this header for all requests which are sent to the above web service. Lets see how we can do this.

Start to create a new Jmeter test plan. Add a thread group and add SOAP/XML-RPC request sampler. Add SOAP envelope and specify the endpoint URL.



Step 3

We need to insert authorization HTTP header to each SOAP request. Therefore, we need to use one of the Config Elements included in Jmeter. HTTP Authorization Manager config element comes in handy in this situation. Authorization manager can be used to specify login information when you access websites, web services or any other HTTP accessible resource which secured with basic authorization.

Select the thread group and select Config Element --> Authorization Manager
HTTP authorization manager config element will be added to your thread group as shown below.



Step 4

Specify the following properties in HTTP Authorization manager.

Base URL = http://localhost:8080/axis2
username = charitha
password = charitha

Here, Base URL is a part or complete URL of the web service you are going to access.
User name and password are the credentials which we specified in tomcat-users.xml file

Step 5

Add a listener and run the test. You will see the SOAP request with the following HTTP headers.

Content-Type: text/xml
SOAPAction: "urn:echoString"
Connection: close
Authorization: Basic Y2hhcml0aGE6Y2hhcml0aGE=
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8080
Content-Length: 268