Saturday, November 24, 2012

Pin It

Widgets

Groovy Scripting - Turn SOAP UI into a complete Automation Tool

Hello Readers,

    Greetings from TechCrooks.com. Today, we will learn about Groovy Scripting and how it can power the SOAP UI Tool, one of the leading Web Services/ API testing tools in the market.

You might be aware of various Web services testing tools like ParaSoft SOATest, SOAP Sonar, Itko LISA, etc. Smart Bear's SOAP UI Pro tool is one among the services testing tools, which has few amazing features like easier property/data transfer, running the scripts in multiple environment/servers, Data Source feature for data parametrization, etc..

SOAP UI Pro tool costs close to 350$ for each of the stand alone licences for a period of 1 year. But the beta version of the tool is free to use with a limited features. But have you ever wondered if you can get all those cool features that you have in the pro version of the tool in beta? If yes, we like your thinking. The tool allows the user to create his/her own scripts using Java/Groovy. Though not all the features can be achieved in beta, most of them can be created using the scripting support that SOAP UI beta provides for free.

"Oh come on. Don't ask me to learn a language now." See, we read your mind. :) Need not panic. Groovy is one of the best scripting languages used to manipulate with the XMLs. You need to have basic Java Knowledge to understand the sytax and data types of the script. Yes, just the basic knowledge.

In this post, we will just see how groovy scripting can be used with SOAP UI beta 4.5.1 tool.

Below are few of the features that you can achieve using Groovy Scripts. We are considering that you already have a little knowledge on the tool.

Features:

1)Data/Property transfers:

Say we have a scenario where we have to pass a value from the response of one method to the request of next method to continue a process.
Eg: Search for a product, and make a payment for a particular product ID from the search product response.

Below is the groovy script to achieve the same:


java.lang.Integer
java.lang.String

def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );

// create holder for Search response

def holder = groovyUtils.getXmlHolder( 'SearchProducts_XMLRequest#ResponseAsXml' );

// create a variable productID to temporarily save the value and provide a valid XPath

def productID = holder.getNodeValue( '//ShoppingSearchResponse[1]/ShoppingSearchResults[1]/Products[1]/Camera[1]/@id' );
log.info('The product ID is : ' + productID );

//Saving the value of productID in the Global properties

com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'PropTransferSearchproductID', productID);


Now the same Global properties value will have the required product ID from the search product response.

The same can be accessed in the Payment product request-> Product Id tag. Below is the implementation:

<productID>${#Global#PropTransferSearchproductID}</productID>

Instead of doing the saving the required value on a global level, it can be saved on various other levels. We will discuss the same in the next point.

2)Assigning and Accessing the property values at various levels:

The properties can be assigned and accessed at 4 different levels in SOAP UI tool.

A)At Global level - A property value saved at a global level can be accessed by any of projects in the Soap UI workspace.

Soap UI Global properties can be accessed from the File Menu->Preferences option->Global Properties tab.




Assigning:
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'PropTransferSearchproductID', productID);

Accessing:(In a XML)
<productID>${#Global#PropTransferSearchproductID}</productID>
Accessing:(In a Groovy)
def GlobalproductID = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue( 'PropTransferSearchproductID');
(or)

def GlobalproductID = context.expand( '${#Global#PropTransferSearchproductID}' );

SOAP UI Global properties are mostly used to drive the endpoint or WSDL, Authentication details for Basic HTTP Authentication, DB Authentication, DB Schema name, etc.. which will be the same for the entire workspace.

B)At Project level - A property value saved at a project level can be accessed by all the testsuites and test cases under that project.



Assigning:
testRunner.testCase.testSuite.project.setPropertyValue( 'PropTransferSearchproductID', productID);


Accessing:(In XML)
<productID>${#Project#PropTransferSearchproductID}</productID>

Accessing:(In a Groovy)
def ProjectproductID = testRunner.testCase.testSuite.project.getPropertyValue( 'PropTransferSearchproductID');

C)At TestSuite level - A property value saved at a testsuite level can be accessed by all the testcases under the designated testsuite.

Assigning:
testRunner.testCase.testSuite.setPropertyValue( 'PropTransferSearchproductID', productID);

Accessing:(In XML)
<productID>${#TestSuite#PropTransferSearchproductID}</productID>
Accessing:(In a Groovy)
def TestSuiteproductID = testRunner.testCase.testSuite.getPropertyValue( 'PropTransferSearchproductID');

D)At TestCase level - A property value saved at a testcase level can be accessed only by that particular test case.

Assigning:
testRunner.testCase.setPropertyValue( 'PropTransferSearchproductID', productID);

Accessing:(In XML)
<productID>${#TestCase#PropTransferSearchproductID}</productID>
Accessing:(In a Groovy)
 def TestCaseproductID = testRunner.testCase.getPropertyValue( 'PropTransferSearchproductID'); 

SOAP UI also allows the user to add a "Properties test step" which is similar to the testcase level property. All the Test Steps under the test case can access al the property values from a Property test step.




Assigning:
def propertyStep = testRunner.setTestCase().getTestSuite().getTestCaseByName('001_testcasename').getTestStepByName('TestStepName')

propertyStep.setPropertyValue('PropTransferSearchproductID', value);


Accessing:(In XML)
<productID>${#PropertiesStepName#PropTransferSearchproductID</productID>

Accessing:(In a Groovy)
def Value = context.expand( '${PropertiesStepName#PropTransferSearchproductID}' );

(or)

 def propertyStep = testRunner.getTestCase().getTestSuite().getTestCaseByName('001_testcasename').getTestStepByName('TestStepName')

def RowStartValue = propertyStep.getPropertyValue('PropTransferSearchproductID');

3)Controlling the flow of execution:

The flow of execution can be controlled by adding a condition in groovy script and forcing the control to go to a particular script. Below is a sample script for the same,

def envvalue = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue( 'environment' )
public int env = envvalue;
env = env - 48
if (env.value == 1)
{
log.info 'Execution on First Environment : Completed'
temp = '2';
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'environment', temp);
testRunner.gotoStepByName( 'ABC_STEP');
}
else if (env.value == 2)
{
log.info 'Execution on SecondEnvironment : Completed'
temp = '3';
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'environment', temp);
testRunner.gotoStepByName( 'XYZ_STEP')
}

4)Reading and manipulating the WSDL's at runtime:

The WSDL's or End point URL's can be accessed at runtime, and the same can be manipulated. Below is a sample script which tells us how to compare two WSDL URL's.
def TextInWSDL = 'Product'
def EndPointURL = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue( 'EndPointURL')

log.info('EndPointURL from Global: ' +  EndPointURL );
def newEndPointURL = EndPointURL.replace('.', '')
newEndPointURL = newEndPointURL.replace('http://', '')
log.info('NewEndPointURL: ' +  newEndPointURL );
def StringAppend = newEndPointURL.getAt(6) + newEndPointURL.getAt(7) + newEndPointURL.getAt(8) + newEndPointURL.getAt(9)
log.info('StringAppend: ' +  StringAppend);
log.info('TextInWSDL: ' +  TextInWSDL + StringAppend);
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'PropTransferBookingIDFormat', TextInWSDL + StringAppend);

Note: For this script to work, the Endpoint URL in your SOAP UI Tool should have been parametrized like ${#Global#EndPointURL}.

5)Running the scripts in multiple environments:

Instead of creating two set of scripts to run on two Endpoint URL's (or two different servers), groovy gives a flexibility to run the same set of scripts on multiple environments.
To Achieve this, we will be making use of the SOAP UI tool's multiple environment feature which is applicable only from SOAP UI v4.5.0 or later.

This is a 3 step process:

1)You have to set a dummy value to your enviroment:
2)You have to select the required environment:
3)You have to loop back to the next environment:

Setting a dummy value for the environment variable:
import java.lang.String;
import java.lang.Integer;

temp = '1';
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'environment', temp);

Selecting Environment at Run time:
def GlobalEnvironmentID = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue( 'Environment');
log.info GlobalEnvironmentID

if (GlobalEnvironmentID .value == 1)
{
testRunner.testCase.testSuite.project.setActiveEnvironment('FirstEnvironment')
log.info 'Running the test on QA_STABLE Build'
}
else if (GlobalEnvironmentID .value == 2)
{
testRunner.testCase.testSuite.project.setActiveEnvironment('SecondEnvironment')
}


Looping back to the next Environment:
 def envvalue = com.eviware.soapui.SoapUI.globalProperties.getPropertyValue( 'environment' )
public int env = envvalue;
env = env - 48
if (env.value == 1)
{
log.info 'Execution on FirstEnvironment : Completed'
temp = '2';
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'environment', temp);
testRunner.gotoStepByName( 'FIRST_TEST_STEP');
}
else if (env.value == 2)
{
log.info 'Execution on SecondEnvironment : Completed'
temp = '3';
com.eviware.soapui.SoapUI.globalProperties.setPropertyValue( 'environment', temp);
testRunner.gotoStepByName( 'LAST_TEST_STEP')
}


6)Groovy Assertions:

SOAP UI Generic assertions cannot assess complex scenarios which include various conditions. Hence a script assertion have to be used.

To access a testcase property in a script assertion:

def testCaseProperty = messageExchange.modelItem.testStep.testCase.getPropertyValue( 'PropertyName' )

Sample Assertion Script:
//Get the XMLStepname
def XMLStepname = messageExchange.modelItem.testStep.getName()
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
// create holder for the response
def holder = groovyUtils.getXmlHolder( XMLStepname + '#ResponseAsXml' );

def xml = new XmlSlurper().parseText(holder.getXml())
// Find the Tag Name and attribute name
def tag= xml.depthFirst().findAll { it.name() == 'TagName' && it.@type == 'AttributeName' }
//Count the total number of tags
def PropertyName= tag.size()
log.info PropertyName


assert PropertyName == 1

Will return an assert value true or false, true if 'PropertyName = 1 and false if PropertyName = any other value'
7)Groovy Data Source loop:

A groovy data source is a custom data source to iterate or parametrize values. Below is a sample script to create a groovy data source:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
def projectPath = groovyUtils.projectPath
def folderName = projectPath + '/testData'
def row = testRunner.testCase.testSteps['DataSource'].currentRow
def allFiles = []
new File( folderName ).eachFile() { file ->
if( file.name =~ /.txt/ )
{
allFiles.add( file.name )  }
}
if ( (row + 1) <= allFiles.size )
{
// output to the TestStep property called inputData
result['inputData'] = new File( folderName + '/' + allFiles[row] ).text
} 

We have not really implemented the groovy data source and data source loop. The above code is just an extract from the official SOAP UI site. For more information on this, please click here.


8)Renaming a request in SOAP workspace at run time:

Below groovy script uses a .setName function to set a test step name at run time.

for( testCase in testRunner.testCase.testSuite.getTestCaseList() )
{
  for( testStep in testCase.getTestStepList() )
    {
      String TestStepName = testStep.getLabel()
      if( testCase.getLabel() == TestCaseName )
       {
        if (TestStepName =~ 'XML1')
         {
           testStep.setName('ABC')
         }
        else if (TestStepName =~ 'XML2')
         {
            testStep.setName('XYZ')
         }
       }
     }
}

Few of the awesome cool features like controlling the flow of execution, Conditional script assertions, Switching Environment at runtime, etc. are available to the tool only through groovy scripting.

Though all these good to have features can be achieved, SOAP UI Pro always allows the user to do things in the most easiest way. To mention a few, XPATH Quert, property transfer using the property transfer environment, support for multiple environments, a datasource step with click and extract data, etc.

Thanks,
Swaminathan.B

Your feedback and comments are most welcomed.

5 comments :


  1. Hi Friends,

    I am using Soap UI 4.5.2 free version. Please observe project structure




    My Project has only one suite
    Suite has two test scenarios here (can have many)
    Here in my project each test step is a test case
    When I run this suite, I want each test scenario should have one excel sheet
    In that excel sheet, I need to get the test case name, request xml, response xml, and status as shown below






    I have tried some groovy scripting; I am sharing the script also. Please do help


    //Author : Vasudev Reddy
    import java.text.SimpleDateFormat
    import java.io.File
    import java.io.FileInputStream
    import java.io.FileOutputStream
    import jxl.*
    import jxl.write.*

    //Get Project Name
    def ProjectName= testRunner.testCase.testSuite.project.name
    //log.info(ProjectName)

    //Get Test Case Name
    def TestCaseName= testRunner.testCase.name
    //log.info(TestCaseName)

    //Current Date
    dateFormat = new java.text.SimpleDateFormat('dd-MMM-yyyy HH-mm-ss')
    def Date = dateFormat.format(new Date())

    //Selecting a folder path
    def folderPath = "C:\\Vasu\\Test\\" + ProjectName +" Results"
    createFolder = new File(folderPath).mkdir()
    // log.info("Folderpath" + folderPath)


    def workbook = Workbook.createWorkbook(new File(folderPath +"\\"+ "Results " + "(" + Date + ")" + ".xls"));

    def soapuiRequests = testRunner.testCase.getTestStepsOfType(com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep)
    soapuiRequests.each{

    def sheet = workbook.createSheet(TestCaseName, 0)
    Label header1 = new Label(0, 0, "Test case Name");
    sheet.addCell(header1);

    Label header2 = new Label(1, 0, "Request XML");
    sheet.addCell(header2);

    Label header3 = new Label(2, 0, "Response XML");
    sheet.addCell(header3);

    Label header4 = new Label(3, 0, "Status");
    sheet.addCell(header4);

    def testName = it.name;

    Label testcasename = new Label(0, 1, testName);
    sheet.addCell(testcasename);

    Label requestxml = new Label(1, 1, context.testCase.getTestStepByName(it.name).getProperty("request").value);
    sheet.addCell(requestxml);

    Label responsexml = new Label(2, 1, context.testCase.getTestStepByName(it.name).getProperty("response").value);
    sheet.addCell(responsexml);

    Label status = new Label(3, 1, testRunner.status.toString());
    sheet.addCell(status);

    workbook.write()
    }

    workbook.close()


    I am able to create the folder, excel file inside the folder, first test case row also,
    But iam not able to get all test case one after another.

    I know I am missing the loop somewhere please do help me
    please do provide me the email address so that i can explian in clear with screenshots and send you mail. iam not able to put the images here to explain the flow

    ReplyDelete
    Replies
    1. Hi Vasudev,

      Were you able to solve your above script issue? Would you mind sharing your expereince and solution with me? I am facing similar issue and would solution would help me proceed.

      Appreciate your help.

      Thanks,
      Vijay

      Delete
  2. Hi Vasudev,
    Sorry for the delay. guess this is your requirement.

    import java.text.SimpleDateFormat
    import java.io.File
    import java.io.FileInputStream
    import java.io.FileOutputStream
    import jxl.*
    import jxl.write.*
    import java.lang.String;
    import java.lang.Integer;
    import java.util.regex.Matcher
    import java.util.regex.Pattern

    //Get Project Name
    def ProjectName= testRunner.testCase.testSuite.project.name
    //log.info(ProjectName)

    //Get Test Case Name
    def TestCaseName= testRunner.testCase.name
    //log.info(TestCaseName)

    //Current Date
    dateFormat = new java.text.SimpleDateFormat('dd-MMM-yyyy HH-mm-ss')
    def Date = dateFormat.format(new Date())

    //Selecting a folder path
    def folderPath = "D:\\CurrencyConvertor_Results\\"
    createFolder = new File(folderPath).mkdir()
    // log.info("Folderpath" + folderPath)


    def workbook = Workbook.createWorkbook(new File(folderPath +"\\"+ "Results " + "(" + Date + ")" + ".xls"));

    def soapuiRequests = testRunner.testCase.getTestStepsOfType(com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep)

    for( testCase in testRunner.testCase.testSuite.getTestCaseList() )
    {
    log.info("Loop 1:" + testCase.getName())
    for( testStep in testCase.getTestStepList() )
    {
    if( testStep.getName() != "Groovy Script")
    {
    log.info("Loop 2:" + testStep.getName())

    def sheet = workbook.createSheet( testStep.getName(), 0)
    Label header1 = new Label(0, 0, "Test case Name");
    sheet.addCell(header1);

    Label header2 = new Label(1, 0, "Request XML");
    sheet.addCell(header2);

    Label header3 = new Label(2, 0, "Response XML");
    sheet.addCell(header3);

    Label header4 = new Label(3, 0, "Status");
    sheet.addCell(header4);

    Label testcasename = new Label(0, 1, testRunner.testCase.getName());
    sheet.addCell(testcasename);

    def request = context.expand( '${'+ testStep.getName()+'#Request}' )
    Label requestxml = new Label(1, 1, request);
    sheet.addCell(requestxml);

    def response = context.expand( '${'+testStep.getName()+'#ResponseAsXml}' )
    Label responsexml = new Label(2, 1, context.expand( '${'+response + '#Response}' ))
    sheet.addCell(responsexml);

    Label status = new Label(3, 1, testRunner.status.toString());
    sheet.addCell(status);
    }
    }
    workbook.write()
    }
    workbook.close()

    ReplyDelete
  3. Hi,

    I am getting following error. do you know whether i am doing something wrong/

    ue Oct 14 16:41:01 EDT 2014:ERROR:An error occurred [startup failed:
    Script2.groovy: 44: unable to resolve class Label
    @ line 44, column 7.
    Label header1 = new Label(0, 0, "Test case Name");
    ^
    org.codehaus.groovy.syntax.SyntaxException: unable to resolve class Label
    @ line 44, column 7.
    at org.codehaus.groovy.ast.ClassCodeVisitorSupport.addError(ClassCodeVisitorSupport.java:146)

    ReplyDelete
  4. Hi Vijay,
    Did you mention all the import statements as per my comment above? The code I had mentioned is a working code.

    Would like to see ur complete code to check where the issue is.

    Thanks,
    Swami

    ReplyDelete