SoapUI

SOAP/REST API Testing Using SoapUI

Created by Kavan Sheth



Best viewed in Chrome, Firefox, Opera or A browser supporting HTML5. Use arrow keys ( up ↑, down ↓, left ←, right ↑) to navigate. Uses Reveal.js by Hakim El Hattab Press ESC to enter the slide overview.

Introduction

  • Market for WebService is growing at fast pace. Now a days mostly all applications exposes APIs to access there services over Web.
  • Services can be implemented either using SOAP(Simple Object Access Protocol) or REST(REpresentational State Transfer).
  • SoapUI is a very good open source and cross-platform Functional Testing Solution.(There is also Paid version of SOAP UI, but be sure before investing for any extra feature whether it is must and worth paying for.)
  • Good thing about SoapUI is it provides capability to perform functional testing as well as load testing.
  • Load Testing is very critical for a Web Services, with proper performance matrices you can be ready to deal with different situations once service is exposed.

Send Single SOAP/REST Request



Installation

  • Installing SoapUI is quite easy. Just download SoapUI Installation from here. Usually you will get redirected to sourceforge for free version of SoapUI.
  • Once installed you will be ready to start with SoapUI.

SoapUI GUI

Once you open SoapUI, you will see something as below:

Send a SOAP Request

Create a new SOAP Project

Here we need two things, 1. Project Name and 2. WSDL

As you must be aware, SOAP services provides interfacing details using WSDL(Web Service Description Language). So you will need a WSDL specifying WebService to be tested.

Send a SOAP Request

Once you create project, you will get Auto Generated Dummy Request as following:

Note: Dummy request is generated based on schema, so it will contain all possible tags, based on comments place you will be able to identify whether a tag is mandatory in schema or not

Send a SOAP Request

A Valid request for my createToken API is something like following:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:ns2="http://soap.crmapi.util.redknee.com/PaymentGatewayIntegration/xsd/2012/03" 
xmlns:pay="http://soap.crmapi.util.redknee.com/PaymentGatewayIntegration/xsd/PaymentGatewayIntegration-v1.0" 
xmlns:ns1="http://soap.crmapi.util.redknee.com/common/xsd/2008/08" 
xmlns:ns="http://soap.crmapi.util.redknee.com/common/xsd/2011/05">
	<soapenv:Header>
	</soapenv:Header>
	<soapenv:Body>
		<pay:createToken>
			<pay:header>
				<ns:password>rkadm</ns:password>
				<ns:username>rkadm</ns:username>
			</pay:header>
			<pay:request>
				<ns2:accountID>19231123344</ns2:accountID>
				<ns2:tokenValue>208</ns2:tokenValue>
				<ns2:maskedCardNumber>3434324324325327</ns2:maskedCardNumber>
				<ns2:expiryDate>201512</ns2:expiryDate>
			</pay:request>
		</pay:createToken>
	</soapenv:Body>
</soapenv:Envelope>

So lets see what we are getting on sending this request.

SOAP Response

On Sending request I am getting following response in SOAP Response Window:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <soapenv:Fault>
         <faultcode>soapenv:Server</faultcode>
         <faultstring>WSDoAllReceiver: Incoming message does not contain required Security header</faultstring>
         <detail>
            <Exception>org.apache.axis2.AxisFault: WSDoAllReceiver: Incoming message does not contain required Security header
	at org.apache.rampart.handler.WSDoAllReceiver.processBasic(WSDoAllReceiver.java:221)
	...<Truncated Exception Detail>
	at java.lang.Thread.run(Thread.java:662)</Exception>
         </detail>
      </soapenv:Fault>
   </soapenv:Body>
</soapenv:Envelope>

Whenever there is some issue Soap server sends a response with faultcode and faultstring. Here it says "Incoming message does not contain required Security header". So here some security headers are expected by SOAP server in soap request. In My case it is username and timestamp token, which are missing, Let's see how to add these tokens to your security header.

Adding Security Header to Request

Double click on your project e.g.

It will open Project Property Window, Select WS-Security Configurations Tab, on this tab select Outgoing WS-Security Configurations tab. It will look like this:

Adding Security Header to Request

Add Token one by one as shown in below GIF:

Adding Security Header to Request

  • Now close your Request and open it again (this is necessary to get new configuration reflected).
  • Click on "Auth" link at left bottom of your request window.
  • It will show "No Authorization" in Authorization drop down.
  • Select Add New Authorization.
  • In new PopUp Window, select "Basic" from the drop-down.
  • Once you select Basic, it will show two more drop-down, "Outgoing WSS" and "Incoming WSS" in bottom.
  • Select new configuration "test" from Outgoing WSS Drop-down.

Adding Security Header to Request

Something like as shown in below GIF:

Sending REST Request Using SoapUI

Sending REST request is simple and similar to SOAP request

I will be using following Request and URL(It is based on an internal project so data is irrelevant here)

URL:http://optimus-z2:11100/AppCrmInvoice/home?cmd=UsageStatementCharge

Method:POST

<request version="1.0">
  <agentID>rkadm</agentID>
  <requestID>1234567890</requestID>
<header>
   <name>username</name>
   <value>test</value>
</header>
<header>
   <name>securityToken</name>
   <value>test</value>
</header>
<parameter>
  <name>subscriberId</name>
  <value>12100765-1</value>
</parameter>
<parameter>
  <name>spid</name>
  <value>1</value>
</parameter>
<parameter>
  <name>usageCriteria</name>
  <value>1|1|0|Local Call|1</value>
</parameter>  
<parameter>
  <name>invoiceId</name>
  <value>TAXSEQID802</value>
</parameter>
<parameter>
  <name>language</name>
  <value>en</value>
</parameter>
<parameter>
  <name>interfaceId</name>
  <value>WSC</value>
</parameter>
</request>

Similar to SOAP, create a new REST Project, you can provide either URL or WADL(Web Application Definition Language, like WSDL).

You can also use RESTClient, a FireFox Extension, to send REST Requests.

Design a TestCase



What usually a TestCase requires?

  • Dataset for your test, you may need to read it from csv file, excel file, database etc.
  • Manipulation of data before use.
  • Actual test (in this case API Request).
  • Data flow between test steps (Result from one API as input to Other API).
  • Looping and Conditional branching.
  • Wait for some action to be completed.
  • Assertions based on Response.
  • Send mail on completion of your activity or on failure.

SoapUI and SoupUI NG Pro(yes, sorry to say paid version) provides these functionality to design your test cases.

If you have enough resources, it is always beneficial to go with Pro.

If you are in other category(not so resourceful) and a bit techie, you can do most of the things using SoapUI free version.

To start with, right click on project and create TestSuit, now right click on TestSuite and create a TestCase. Right click on TestCase and click on "Add Step". Following are steps in SoapUI Free and in Pro version:

Property

  • Property is most important part in designing a testcase because it takes care of data flow between test steps
  • There are three kind of properties:
    • Project level default/custom properties
    • Test suite level default/custom properties
    • Test Case level default/custom properties
  • Default Properties: As name suggesting these are default properties created with the different element, If you select Project, there will be default project level properties like Name, Description, File etc...
  • Custom Properties: Any properties you define,
    • Under property tab for Project, TestSuite or TestCase Editors
    • By adding properties step at any level
    • Generate properties using DataSource Step

Property - Cont.

  • Now General Way to access these properties is ${#level#propertyName}
  • e.g:
    • To read TestCase level property : ${#TestCase#param1}
    • To read TestSuite level property : ${#TestSuite#param1}
    • To read Project level property : ${#Project#param1}
  • So if you have property defined you can use it into your soap request as following:

Property Transfer

  • Property Transfer is another most important functionality, mostly it is used to transfer a value to property so it can be used later in TestCase.
  • Let's take an example, Consider following Request and Response:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sub="http://soap.crmapi.util.redknee.com/subscriptions/xsd/Subscriptions-v2.0" xmlns:ns="http://soap.crmapi.util.redknee.com/common/xsd/2011/05" xmlns:ns1="http://soap.crmapi.util.redknee.com/common/xsd/2008/08" xmlns:ns2="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2011/01">
   <soapenv:Header/>
   <soapenv:Body>
      <sub:getSubscriptionBalance>
         <sub:header>
            <ns:password>ltups30</ns:password>
            <ns:username>rkadm</ns:username>
         </sub:header>
         <sub:subscriptionRef>
            <ns2:accountID>${#TestCase#ban}</ns2:accountID>
            <ns2:identifier>${#TestCase#ban}-1</ns2:identifier>
            <ns2:spid>1</ns2:spid>
            <ns2:subscriptionType>10001</ns2:subscriptionType>
            <ns2:state>1</ns2:state>
         </sub:subscriptionRef>
      </sub:getSubscriptionBalance>
   </soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <ns14:getSubscriptionBalanceResponse xmlns:ns14="http://soap.crmapi.util.redknee.com/subscriptions/xsd/Subscriptions-v1.2">
         <ns14:return>
            <ns10:amount xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">0</ns10:amount>
            <ns10:expiryDate xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">2015-06-07T00:00:00.000-04:00</ns10:expiryDate>
            <ns10:givenMobileNumber xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">7700085225</ns10:givenMobileNumber>
            <ns10:primaryMobileNumber xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">7700085225</ns10:primaryMobileNumber>
            <ns10:currency xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">CAD</ns10:currency>
            <ns10:blockedBalance xmlns:ns10="http://soap.crmapi.util.redknee.com/subscriptions/xsd/2010/06">0</ns10:blockedBalance>
         </ns14:return>
      </ns14:getSubscriptionBalanceResponse>
   </soapenv:Body>
</soapenv:Envelope>

and target is to store expiryDate from response in a property to use it later.

Property Transfer

Note: In free version, you will not get outlined response to extract desired xml tag, Need to create xpath manually and add required namespaces.

Groovy Script

  • Groovy script is one of the most important steps, It provides power to do almost anything like
    • Modify Request/Response,
    • Manipulate your data set,
    • Read your dataset from different sources,
    • Generate dataSet
  • If you are using free version of SoapUI, Groovy script will be most useful thing to achieve most complex tasks

Groovy Script - Access Property in Script.

  • To access property in script use context or testRunner
    def param1 = testRunner.testCase.getPropertyValue( "param1" )
    def param1 = context.testCase.testSuite.getPropertyValue( "param1" )
    def param1 = testRunner.testCase.testSuite.project.getPropertyValue( "param1" )
    Or
    def param1 = context.expand('${#Project#param1}')
    def param1 = context.expand('${#TestCase#param1}')
    def param1 = context.expand('${#TestSuite#param1}')
  • Similarly you can set property as following:
  • context.testCase.setPropertyValue( "param1","xyz" )
    context.testCase.testSuite.setPropertyValue( "param1","xyz" )
    context.testCase.testSuite.project.setPropertyValue( "param1","xyz" )

Groovy Script - Cont.

  • Just to get idea following is a Groovy script, which generates a random number within given range and store it into a predefined TestCase level property and print it to log.
  • def myTestCase = context.testCase
    				
    int x = Math.random()*100000 + 93000000
    
    myTestCase.setPropertyValue("ban", x.toString())
    
    log.info context.expand('${#TestCase#ban}')
    				

DataSource(Pro feature)

Here I will not go into details of each Data Source, because it is trivial to use any data source and it is always good to know what can be done, how to do can be figured out on need to know basis.

As you can see, SoapUI NG Pro supports almost all possible datasource configurations, using them is too easy. Don't loose heart, SoapUI free provides groovy script and many thing can be achieved using groovy scripts, so here I will list out how to accomplish same datasource configurations using Groovy.

DataSource - JDBC

import groovy.sql.Sql

//Need to register driver first
//for SQL Server use: com.microsoft.sqlserver.jdbc.SQLServerDriver
//for MySQL: com.mysql.jdbc.Driver
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver("oracle.jdbc.driver.OracleDriver")

con = Sql.newInstance("jdbc:oracle:thin:@DBHost:1521:DBName", "DBUser", "DBPasword")

def res = con.rows("SELECT sysdate FROM dual")

log.info(res[0].sysdate.toString())

con.close()
Note: Any external jar that should be added to SoapUI classpath, needs to be added in bin/ext folder(In this case ojdbc6.jar).

DataSource - File

//reading all txt file at once

File file = new File("D://user.txt")
fileContent = file.getText()                 
log.info fileContent
//reading text line by line

File file1 = new File("D://user.txt")
List textLine = file1.readLines()
log.info textLine
//reading one line at a time

context.fileReader = new BufferedReader(new FileReader("C:\\PerTableData\\TestData.csv"))
//Read in the first line of the data file
nextLine = context.fileReader.readLine()
while(nextLine != null){
	nextLine = context.fileReader.readLine()
	log.info nextLine
}

DataSource - Excel

import jxl.*
import jxl.write.*
Workbook workbook1 = Workbook.getWorkbook(new File("c:\\some.xls"))
Sheet sheet1 = workbook1.getSheet(0)
Cell a1 = sheet1.getCell(0,2) // getCell(row,column) -- place some values in myfile.xls
Cell b2 = sheet1.getCell(2,2)  // then those values will be acessed using a1, b2 & c3 Cell.
Cell c2 = sheet1.getCell(2,1)
log.info a1.getContents()
log.info b2.getContents()
log.info c2.getContents()
workbook1.close()
Note: Need to place jxl.jar under bin/ext folder, jxl works with xls only and not with .xlsx, You can use Apache POI if want to read .xlsx

DataSource - XML

Consider an xml with multiple Employee tags, each Employee tag have age and name as sub elements. Following script will print name of each Employee having age > 19.

//Read and parse XML file and store it into a variable
def InputXML = new XmlParser().parseText(inputFile.text)
//Find - Filter XML nodes based on a condition
def InputRow = InputXML.Employee.findAll{
    it.Age.text().toInteger() > 19;   
     //We are finding all XML nodes where the age > 19
}
InputRow.each{
    //Display the value of name node from the filtered record
    log.info(it.Name.text());
}
So Similarly you can try/find for other dataSources. Main point is Groovy Script Rocks :).

DataSource Loop

Another important step in SoapUI NG Pro is DataSource loop.

Data read from DataSources represents different possible input to a TestCase, so we need to run TestCase with each of the data retrieved and for that we need to create loop to instantiate our TestCase with different data set.

Found a good article here.

We need three steps here:

  • Properties to handle looping
  • a DataSource(Of course using Groovy Script)
  • a DataLoop(Groovy only)

Any of your repetitive TestSteps should go between DataSource and DataLoop

Properties to handle looping



Here we need to define three properties. with Empty values.

count - counter used for looping

value - contains value from dataSource(here value from a file). you can add as many properties as you want.

stoploop - a string used in DataLoop Step to determine whether to stop or move back to initial step

GroovyScript-DataSource

def myTestCase = context.testCase
def counter,size,stoploop,tempValue

//make sure input.txt file already exists and 
//contains different set of values separated by new line (CR).
File fileData = new File("c://redknee/input.txt") 
List lines = fileData.readLines()
size = lines.size.toInteger()

//Read Properties and Initialize if first iteration
propTestStep = myTestCase.getTestStepByName("Property-Looper") 
cnt=propTestStep.getPropertyValue("count")
counter=(cnt==""? 1: cnt.toInteger() )
stoploop="F"

//loop condition
if(counter<=size)
{
	tempValue = lines[counter-1]
	log.info tempValue
	if (counter == size)
		stoploop="T"
	counter=counter+1;
}

//update property values
propTestStep.setPropertyValue("value", tempValue.toString())
propTestStep.setPropertyValue("count", counter.toString() )
propTestStep.setPropertyValue("stoploop", stoploop.toString())

GroovyScript-DataLoop

def myTestCase = context.testCase
propTestStep = myTestCase.getTestStepByName("Property-Looper") 
endLoop = propTestStep.getPropertyValue("stoploop").toString()
if (endLoop.toString() == "T")
{
 log.info ("Exit Groovy Data Source Looper")
 propTestStep.setPropertyValue("value", "")
 propTestStep.setPropertyValue("count", "") 
 propTestStep.setPropertyValue("stoploop", "") 
 assert true
}
else
 testRunner.gotoStepByName("GroovyScript-DataSource") //setStartStep

So using groovy most of the things can be achieved, just need to look around.

Load Testing



Good thing about SoupUI is, there is almost no overhead for load test once you have your TestCase ready.

Main thing about load testing is the strategy for Load.

As you can see in image, here loadTest is run for 5 threads, with "Simple" Strategy, TestDelay 1000, TotalRuns limit 10.

Load Strategies

  • Simple - "The Simple Strategy runs the specified number of threads with the specified delay between each run to simulate a breathing space for the server."
  • Burst - "it does nothing for the configured Burst Delay, then runs the configured number of threads for the “Burst Duration” and goes back to sleep."
  • Thread(RampUp) - "Gradually increase thread from min to max."
  • Variance - "Generate load in sawtooth manor."
  • Fixed(Pro) - "TPS will be maintained by varying threads. TPS can be Testcase Level or Request Level."
  • Script(Pro) - "Decide threads using Groovy Script."
  • Grid(Pro) - "This strategy allows the user to specifically configure the relative change in the number of threads over a defined interval of time."
  • Custom(Pro) - "Draw a graph and load will be generated accordingly."

Load Strategies


Following image may help you understand the load patterns:

Load Test Assertions

Load Test Assertions are very important feature. Using LoadTest Assertions you can fail your Load test if it is not meeting some criteria, like avg time for a particular step exceeds permissible limit.

SoapUI on Unix

  • Many time you would like to avoid network delays for your load test. and for that it might be required to run test from same location where your server resides.
  • Some times you might not have luxury of having windows server available, in such cases you can use Unix/Linux version of SOAP UI.
  • So download Linux binaries of SoapUI(I am using SoapUI-5.1.3-linux-bin.tar.gz) from here.
  • Transfer downloaded tar.gz file to Unix/Linux system, Unzip it (gunzip SoapUI-5.1.3-linux-bin.tar.gz and tar -xvf SoapUI-5.1.3-linux-bin.tar).
  • SoapUI projects are just stored in .xml file, and you can get path for xml file from project level default property "File". for me it is "C:\Users\XXXXXXX\Documents\loadtest-soapui-project.xml"
  • Transfer project xml file to Unix machine. Also need to transfer any required jars placed under bin/ext folder to same folder under Unix machine.

SoapUI on Unix

Once done with setup, you can run your load test as following:

sh SoapUI-5.1.3/bin/loadtestrunner.sh 
-s"TestSuite Name" 
-c"TestCase Name" 
-l"LoadTest Name" 
-r 
-f/path/for/reports/ 
 loadtest-soapui-project.xml

Once LoadTest is over, you will find load test statistics and error logs under path provided with -f argument.

$ cat LoadTest_1-log.txt
time,type,step,message
2015-05-21 23:54:51,Message,,"LoadTest started at Thu May 21 23:54:51 IST 2015"
2015-05-21 23:55:32,Message,,"LoadTest ended at Thu May 21 23:55:32 IST 2015"
$ cat LoadTest_1-statistics.txt
Test Step,min,max,avg,last,cnt,tps,bytes,bps,err,rat
banSelection,1,128,0.93,0,160,3.83,0,0,0,0
getSubscriptionBalance - Request 1,436,2699,528.77,490,160,3.83,174720,4188,0,0
TestCase:,437,2827,529.7,490,160,3.83,174720,4188,0,0
$