One of the neat things which SoapUI Pro offers is called data driven testing http://www.soapui.org/Data-Driven-Testing/functional-tests.html. The idea is that you can run a test several times and pull data out of a data source (file, database etc) to use in your tests. Sadly not all of us have SoapUI Pro but it's entirely possible to do something similar with SoapUI free (as Theo Marinos demonstrates in his excellent blog on the subject).
The above blog chooses a random value from a file each time you run a test in order to test a service. This is great for load testing, or if all you want to prove is that a service works correctly with one of a given set of values, but what if you want to test that for a given input (or inputs) to the service returns a given output (or outputs). Well that's also quite easy to do to. I've put together a simple example which reads every value in a csv file, calls a web service using the input value and ensures the response contains the expected response from the same line of the file.
Creating a service to test
As I didn't have a real service to test, the first thing I did was to create a SoapUI mock test service based on a very simple WSDL. In the mock service I put some groovy to generate a response based on the input messagedef holder = new com.eviware.soapui.support.XmlHolder( mockRequest.requestContent )This generates a "greeting" variable. If the "firstName" element starts with Mr then it responds "Hello <name>", otherwise it replies "Howdy <name>" - simple. Then in the mock service I put this greeting variable to use:
def name = holder["firstName"];
if (name.substring(0,2)=="Mr")
requestContext.greeting="Hello "+name;
else
requestContext.greeting="Howdy "+name;
<soapenv:Envelope xmlns:xsi="http:www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice">
<soapenv:Header/>
<soapenv:Body>
<urn:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<greeting xsi:type="xsd:string">${greeting}</greeting>
</urn:sayHelloResponse>
</soapenv:Body>
</soapenv:Envelope>
Creating some test data
OK so now we have something to test lets generate some test data. A simple CSV:req_firstName, resp_greeting
Bob, Howdy Bob
Fred, Howdy Fred
Mr Jones, Hello Mr Jones
Jill, Howdy Jill
The first line is the name of the properties these will be stored in, the other lines are the values of those variables. As will become apparent in a second the groovy is completely generic and doesn't need changing as it can read any number of values into variables based on the first line.
So now we have a service to test and some data we need to setup a test...
Configuring the test
I setup a project with a very simple Test Case. It only has 4 test steps:
- A groovy script to load the given values from the file
- A soap test step to call the mock web service
- A conditional goto step to return to the start
- A step called "End" (the name here is important as we'll see below but the step can be of any type - in my case I used a delay test step)
- TEST_FILE: the name (with path) of the csv file created above
- One property for each column in the CSV (with the same name as the header row i,e, req_firstName and resp_greeting)
1. Groovy
tc = testRunner.testCase;The above can be pasted into a groovy step unchanged for any number of properties read from any csv data file.
// First run through: initiation
if (context.fileReader == null){
log.info ("##Starting Data run... File="+tc.getPropertyValue("TEST_FILE").toString());
context.fileReader = new BufferedReader(new FileReader(tc.getPropertyValue("TEST_FILE")));
line = (String)context.fileReader.readLine();
// Get variable names
context.variableNames = line.split(",");
}
// Process each line
context.valueLine = context.fileReader.readLine();
// Data to process: load values
if (context.valueLine!=null){
// Assign the parts of the line to the properties
for (int i=0;i<context.variableNames.length;i++){
variable = context.variableNames[i].trim();
value= context.valueLine.split(",")[i].trim();
log.info "Assigning: $variable=$value";
tc.setPropertyValue(variable, value);
}
}
// No data to process: tidy up
else{
context.fileReader.close();
context.fileReader=null;
context.variableNames=null;
testRunner.gotoStepByName("End");
}
2. SOAP Test
My soap test step request contains a reference to the request property:<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:examples:helloservice">The assertion in the test script (of type xpath) checks the response has the right value in it (again based on the csv value.
<soapenv:Header/>
<soapenv:Body>
<urn:sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<firstName xsi:type="xsd:string">${#TestCase#req_firstname}</firstName>
</urn:sayHello>
</soapenv:Body>
</soapenv:Envelope>
Declare:
declare namespace urn='urn:examples:helloservice';Expected Result:
declare namespace soapenv='http://schemas.xmlsoap.org/soap/envelope/';
/soapenv:Envelope/soapenv:Body/urn:sayHelloResponse/greeting
${#TestCase#resp_greeting}
3. Conditional goto
The goto step should always go back to the groovy step. When all the rows in the file are completed, the groovy code above will jump to "End" and thus stop the loop - this is why the end test step has to be called "End". In order to make the conditional goto unconditional we just need to make the condition always true - by use of the xpath true() function.4. End
Finally the End step is just needed so they groovy script can jump out of the loop. It doesn't matter what this step does so long as there is a step called "End" after the goto step.Running the test
So as the above shows there were 14 test steps (the first 3 steps executed four times for the four people in the csv, then the first and last steps executed to end the cycle). The request and response variables contain the values of the last line in the file as I didn't clear them. From here I could add more variables to the file or add a different set of tests - each test case is self contained so each one can have a different TEST_FILE property and different variables. Simple.If the service didn't give the expected response to one of the values then the soap test step would fail because of the assertion.