addAssertion() not recognizing string value of "Contains" type - groovy

I have a groovy script that I've added code to to check if a value exists in an XML response I'm getting within SoapUI. I've been dealing with this for a few days and could use some help.
Here is the code:
import com.eviware.soapui.support.XmlHolder
import com.eviware.soapui.impl.wsdl.teststeps.registry.RestRequestStepFactory
// read your request template
def requestFile = new File("C:/XMLRequestScript/file.xml");
// parse it as xml
def requestXml = new XmlHolder(requestFile.text)
// get the current testCase to add testSteps later
def tc = testRunner.testCase;
// get the testStep as template to create the other requests
def tsTemplate = tc.getTestStepByName("Template");
// loop to create # of testSteps for each application
for(int i = 1; i < 3; i++) {
// xpath expression to get applicationNumber attribute in root node
def xpathNodeAttr = "/*/#ApplicationNumber";
// get the root node attribute applicationNumber throught an XPATH
int appId = Integer.parseInt(requestXml.getNodeValue(xpathNodeAttr));
// add 1 to appId
appId++;
// set the value again in the attribute
requestXml.setNodeValue(xpathNodeAttr,appId);
// create next testStepName for new Application
def testStepName = "TestStep_ApplicationNumber_" + String.valueOf(appId)
log.info testStepName;
log.info testStepName.getClass().getName()
log.info tc.getClass().getName()
// create a new testStepConfig
def testStepFactory = new RestRequestStepFactory();
def testStepConfig = testStepFactory.createConfig( tsTemplate.getTestRequest(), testStepName )
// add the new testStep to TestCase
def newTestStep = tc.insertTestStep( testStepConfig, -1 )
// set the request
newTestStep.getTestRequest().setRequestContent(requestXml.getXml())
// add Assertions here
def assertion = tc.getTestStepByName(newTestStep.toString()).addAssertion("Contains")
if (assertion.contains("Champion5")) {
newTestStep.addAssertion("Champion5")
log.info("REST response assertion created into: " + newTestStep)
} else {
log.info("REST response assertion not found in " + newTestStep)
}
if (assertion.contains("Challenger5")) {
newTestStep.addAssertion("Challenger5")
log.info("REST response assertion created into: " + newTestStep)
} else {
log.info("REST response assertion not found in " + newTestStep)
}
// execute the request
newTestStep.run(testRunner, context)
}
In the section above called "// add Assertions here", the line of code that I'm having a problem with is:
def assertion = tc.getTestStepByName(newTestStep.toString()).addAssertion("Contains")
The error states:
Thu Sep 18 14:27:57 CDT 2014:ERROR:An error occurred [Cannot invoke method addAssertion() on null object], see error log for details
My understanding is that I have to pass an Assertion Type that is contained in the SoapUI GUI and just put it in as a string value in order to check if the values that I'm checking are asserted.
Any suggestions/direction would be appreciated. I have no idea what I'm doing wrong. Thanks.

I think the problem is your are calling the function tc.getTestStepByName(String name) with a wrong name, because the newTestStep.toString() doesn't return the Test step name instead it returns the class name (it really returns 'classname' + '#' + 'Integer.toHexString(hashCode())' if it's not override it, see Object.toString()) .
Anyway what you want is to add an assertion for a test step which is just created so add the assertion directly to it instead to find it by name, use:
def assertion = newTestStep.addAssertion("Contains")
instead of:
def assertion = tc.getTestStepByName(newTestStep.toString()).addAssertion("Contains")
The second problem you have is that you're using the assertion incorrectly. The assertion object in your case is an instance of SimpleContainsAssertion and contains() method doesn't exist in this class, to set the string to check in contains assert use setToken() method, I think that you need this to add the two assertions:
def assertion = newTestStep.addAssertion("Contains")
// add token to check
assertion.setToken("Champion5")
// change assert name in order to avoid the popup
// when we create the second one.
assertion.setName("CONTAINS 1")
// create a second assertion
assertion = newTestStep.addAssertion("Contains")
assertion.setToken("Challenger5")
assertion.setName("CONTAINS 2")
instead of :
// add Assertions here
def assertion = tc.getTestStepByName(newTestStep.toString()).addAssertion("Contains")
if (assertion.contains("Champion5")) {
newTestStep.addAssertion("Champion5")
log.info("REST response assertion created into: " + newTestStep)
} else {
log.info("REST response assertion not found in " + newTestStep)
}
if (assertion.contains("Challenger5")) {
newTestStep.addAssertion("Challenger5")
log.info("REST response assertion created into: " + newTestStep)
} else {
log.info("REST response assertion not found in " + newTestStep)
}
This code will result creating this two asserts, one named "CONTAINS 1" and check that response contains the string "Champion5", and a second one named "CONTAINS 2" and check that response contains the string "Challenge5".
Hope this helps,

Related

Groovy to validate the list of node values with the values stored in a property step

Properties ScreenshotResponse Snippet
<tns:TAResponse
xmlns:tns="http://webconnectivity.co.uk/">
<tns:SessionToken>84hjfutryh47849dkdhg9493493=</tns:SessionToken>
<tns:PartyId>1234</tns:PartyId>
<tns:ResponseType></tns:ResponseType>
<tns:MessageUUId>12341F17-ABC9-3E99-1D12-B8289POO2107</tns:MessageUUId>
<tns:Matches>
<tns:IntegrationServiceMatch>
<tns:InternalReference>2066856</tns:InternalReference>
<tns:ErrorsAndWarnings>
<tns:IntegrationServiceErrorCode>
<tns:ErrorCode>W000026</tns:ErrorCode>
<tns:Description>Settlement Date not within tolerance</tns:Description>
</tns:IntegrationServiceErrorCode>
<tns:IntegrationServiceErrorCode>
<tns:ErrorCode>E000033</tns:ErrorCode>
<tns:Description>Number on message does not match</tns:Description>
</tns:IntegrationServiceErrorCode>
<tns:IntegrationServiceErrorCode>
<tns:ErrorCode>E000001</tns:ErrorCode>
<tns:Description>NO likely matches</tns:Description>
</tns:IntegrationServiceErrorCode>
</tns:ErrorsAndWarnings>
</tns:IntegrationServiceMatch>
</tns:Matches>
</tns:TAResponse>
I have stored all these Errorcode and Description in a property step. I need to validate whether these Errorcode and Description in the response matches with the property step. (sequence is not a deal)
Can someone pls guide me through groovy?
Previously i had to validate only one errorcode and desc from the response. so i validated using below script.
def content = new XmlSlurper().parse(file)
def respType = content.Body.TAResponse.ResponseType.text()
def errAndWarn = content.Body.TAResponse.Matches.IntegrationServiceMatch
assert errAndWarn.size() == 1
for (def i=0;i<errAndWarn.size();i++)
{
assert errAndWarn[i].ErrorsAndWarnings.IntegrationServiceErrorCode.ErrorCode == "E000033"
assert errAndWarn[i].ErrorsAndWarnings.IntegrationServiceErrorCode.Description == "Number on message does not match"
}
You can use below Script Assertion for the SOAP Request test step itself, that will avoid additional Groovy Script test step.
This assumes that the name of the Properties test step name is Properties. If its name differs, then change in the script accordingly.
Script Assertion
//Change the name of the Properties test step below
def step = context.testCase.testSteps['Properties']
//check if the response is not empy
assert context.response, 'Response is empty or null'
//Parse the xml
def parsedXml = new XmlSlurper().parseText(context.response)
//Get the all the error details from the response as map
def errorDetails = parsedXml.'**'.findAll { it.name() == 'IntegrationServiceErrorCode'}.inject([:]){map, entry -> map[entry.ErrorCode.text()] = entry.Description.text(); map }
log.info "Error details from response : ${errorDetails}"
//loop thru xml error codes and verify against the properties of Properties Test Step
errorDetails.each { key, value ->
assert step.properties[key]?.value == value, "Unable to match value of the property ${key} "
}
EDIT: based on OP's comment
Groovy Script test step:
//Change the name of the Properties test step below
def step = context.testCase.testSteps['Properties']
//Parse the xml like you have in your script
def parsedXml = new XmlSlurper().parse(file)
//Get the all the error details from the response as map
def errorDetails = parsedXml.'**'.findAll { it.name() == 'IntegrationServiceErrorCode'}.inject([:]){map, entry -> map[entry.ErrorCode.text()] = entry.Description.text(); map }
log.info "Error details from response : ${errorDetails}"
//loop thru xml error codes and verify against the properties of Properties Test Step
errorDetails.each { key, value ->
assert step.properties[key]?.value == value, "Unable to match value of the property ${key} "
}
EDIT2: Based on additional requirement, adding below Groovy Script
//Change the name of the Properties test step below
def step = context.testCase.testSteps['Properties']
//Parse the xml like you have in your script
def parsedXml = new XmlSlurper().parse(file)
//Get the all the error details from the response as map
def errorDetails = parsedXml.'**'.findAll { it.name() == 'IntegrationServiceErrorCode'}.inject([:]){map, entry -> map[entry.ErrorCode.text()] = entry.Description.text(); map }
log.info "Error details from response : ${errorDetails}"
def failureMessage = new StringBuffer()
//Loop thru properties of Property step and check against the response
step.properties.keySet().each { key ->
if (errorDetails.containsKey(key)) {
step.properties[key]?.value == errorDetails[key] ?: failureMessage.append("Response error code discription mismatch. expected [${step.properties[key]?.value}] vs actual [${errorDetails[key]}]")
} else {
failureMessage.append("Response does not have error code ${key}")
}
}
if (failureMessage.toString()) {
throw new Error(failureMessage.toString())
}
You should be able to do:
content.Body
.TAResponse
.Matches
.IntegrationServiceMatch
.ErrorsAndWarnings
.IntegrationServiceErrorCode.each { node ->
println "Error code is ${node.ErrorCode.text()} and Description is ${node.Description.text()}"
}

how to write all Soap Request available in Project using Soap UI Groovy

I have a soap project with 4 Test Suite,each Test Suite has some Test Case and each Test case has some test steps[Soap Request,Groovy Script]
I am able to access all the properties using mentioned code below , HOWEVER,code is writing blank request/response file in local system **
def Project = testRunner.testCase.testSuite.project;
for(def i=0;i<Project.testSuiteCount;i++)
{
log.info Project.getTestSuiteAt(i).name
def Suite = Project.getTestSuiteAt(i)
for(def j=0;j<Suite.testCaseCount;j++)
{
log.info Suite.getTestCaseAt(j).name
def TCase = Suite.getTestCaseAt(j)
for(def k=0;k < TCase.testStepCount;k++)
{
def TStep= TCase.getTestStepAt(k)
def req = context.expand('${'+TStep.name+'#Request}')
new File("D:/Directory/"+Suite.name+"_"+TCase.name+"_"+TStep.name+"_"+k+".txt").write( req )
}
}
}
** plz help to solve this, **
Actually there are multiple approaches to achieve this.
Below is the one of the approach.
Here is Groovy Script which writes the requests and responses.
This script assumes that user has already run the test suites so that responses are available to be saved.
Create a new test suite -> test case -> add Groovy Script test step and copy the below script into it.
Groovy Script:
/**
* This groovy script saves the request and response
* And this script will be able to write the responses
* into files if and only if there is response available
**/
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep
//Change direcoty path if required
def directoryToSave = 'D:/directory'
//Not required to change the script beyond this point
//date time is appended to the file name
def dt = new Date().format('yyyyMMdd_HHmmss')
//Closure to save the file
def saveToFile(file, content) {
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
log.info "Directory did not exist, created"
}
if (content) {
log.info "Writing the content into file :${file.name}"
file.write(content)
assert file.exists(), "${file.name} not created"
} else {
log.warn "the content is empty, not writing the content into file"
}
}
//Get the project object
def project = context.testCase.testSuite.project
//Loop thru the project and save the request and responses
project.testSuiteList.each { suite ->
suite.testCaseList.each { kase ->
kase.testStepList.each { step ->
if (step instanceof WsdlTestRequestStep) {
def reqFilePath = new File("${directoryToSave}/${suite.name}_${kase.name}_${step.name}_request${dt}.xml")
def resFilePath = new File("${directoryToSave}/${suite.name}_${kase.name}_${step.name}_response${dt}.xml")
saveToFile(reqFilePath, step.testRequest.requestContent)
saveToFile(resFilePath, step.testRequest.responseContent)
saveToFile(step)
} else {
log.info "Ignoring as the step type is not Soap request"
}
}
}
}

groovy.lang.MissingPropertyException: No such property: file for class: Script7 error at line 5

I am writing groovy script to save raw soap request & response and i get this error:
groovy.lang.MissingPropertyException: No such property: file for class: Script7 error at line 5
Here is the Script:
def myOutFile = context.expand( '${#TestSuite#fileName}' )+"_PostPaid-Success_Payment_BillInqReq.xml"
def response = context.expand( '${BillInq#Request}' )
def f = new File(myOutFile)
f.write(response, "UTF-8")
file.write(context.rawRequest,'utf-8')
Please follow the steps below:
Go to Test Suite PostPaid
Add a custom property say DATA_STORE_PATH and its value to a directory name where you like to save the requests and responses
Go to test case PostPaid_Success_Payment
Disable the Step 2 & Step 3 i.e., SaveInquiryReq and SaveInquiryResponse steps. or you may remove altoger as well if no other work is done apart from save the request & response respectively.
Click on step1 BillInq, click on assertions, Choose Script Assertion, see here for more details how to add script assertion
Have below script and click ok
Now you run the step1, you should be able to see the request and responses saved in the above mentioned directory
/**
* This script logs both request and response
*/
assert context.response, "Response is empty or null"
assert context.request, "Request is empty or null"
//Save the contents to a file
def saveToFile(file, content) {
if (!file.parentFile.exists()) {
file.parentFile.mkdirs()
log.info "Directory did not exist, created"
}
file.write(content)
assert file.exists(), "${file.name} not created"
}
def dirToStore = context.expand('${#TestSuite#DATA_STORE_PATH}')
def currentStepName = context.currentStep.name
def requestFileName = "${dirToStore}/${currentStepName}_request.xml"
def responseFileName = "${dirToStore}/${currentStepName}_response.xml"
//Save request & response to directory
saveToFile(new File(requestFileName), context.rawRequest)
saveToFile(new File(responseFileName), context.response)

Groovy Reusable Functions - Extract Node Value - groovy.lang.MissingMethodException:

I'm a Groovy noob and trying to get my head around using reusable functions to extract an xml node value for a given test step and node in SoapUI. It seems the class runs fine but the problem is when using the method. I get the following error:
groovy.lang.MissingMethodException: No signature of method: org.apache.log4j.Logger.info() is applicable for argument types: (java.lang.String, java.lang.String) values: [return TestStepName, Node] Possible solutions: info(java.lang.Object), info(java.lang.Object, java.lang.Throwable), any(), wait(), dump(), find(groovy.lang.Closure) error at line:
This is my class:
class Example
{
def log
def context
def responseSOAXmlStep
def resultValue
def responseNodePath
def storeProperty
// Class constructor with same case as Class name
def Example(logIn,contextIn,testRunnerIn)
{
this.log = logIn
this.context = contextIn
this.responseSOAXmlStep = responseSOAXmlStep
this.responseNodePath = responseNodePath
}
def execute(responseSOAXmlStep,responseNodePath)
{
def groovyUtils = new com.eviware.soapui.support.GroovyUtils( context );
// do some stuff to prove I've run with right context, etc.
log.info "return "+responseSOAXmlStep,responseNodePath
def holder = groovyUtils.getXmlHolder( responseSOAXmlStep+"#ResponseAsXml" );
resultValue = holder.getNodeValue( "//ns1:"+responseNodePath );
log.info("Node value: " + resultValue );
testRunner.testCase.testSuite.setPropertyValue(storeProperty, resultValue);
return execute
}
}
context.setProperty( "example", new Example( log, context, testRunner) )
log.info "Library Context:"+context
This is where I do the call in a step after the response step:
// get a reference to the library TestSuite
library = testRunner.testCase.testSuite.project.testSuites["Library"]
// find the module within the library
module = library.testCases["Library"].testSteps["Second Example_Class"]
// initialise the library; which places an instance of Example in the context
module.run(testRunner, context)
// get the instance of example from the context.
def example = context.example
// run the method, with parameter
log.info "example.execute(responseSOAXmlStep,responseNodePath) = " + example.execute("TestStepName","Node")
I've searched the forum but could not find an answer that suites my query. Any form of assistance is appreciated. Thanks.
Your error description says that you're invoking info() method on log passing two strings as and arguments, and this method doesn't exist.
The problem is easy to solve, pass one concatenated string with + as parameter instead of passing two strings. In your execute method inside your Example class use this:
def execute(responseSOAXmlStep,responseNodePath)
{
...
// USE + TO CONCATENATE STRINGS INSTEAD OF USE ,
log.info "return " + responseSOAXmlStep + responseNodePath
...
}
instead of:
def execute(responseSOAXmlStep,responseNodePath)
{
...
// do some stuff to prove I've run with right context, etc.
log.info "return " + responseSOAXmlStep,responseNodePath
...
}
EDIT:
As you said in your comment probably you've another problem storing properties in TestSuite level. You're using this code:
testRunner.testCase.testSuite.setPropertyValue(storeProperty, resultValue);
The problem is that setPropertyValue is expecting string as a first argument however at this line in your code storeProperty is not defined yet. Try for example defining storeProperty as:
def storeProperty = "myProperty" before using it in setPropertyValue call.
Hope this helps,

Executing a Java jar file from within a SoapUI groovy script not working

I am new to SoapUI and Groovy so please forgive this post as it has been posted a number of times in Stackoverflow however I cannot find a fix.
SoapUI version: 4.5.2
I have 2 questions if you guys don't mind:
I have an executable jar file that I've put in the the \bin\ext directory along with another jar that is considered a dependency jar within the code in the jar so I hope it will reference there. The groovy code I found in Stackoverflow that should execute this jar is as follows but does not work as I don't see any output anywhere in the SoapUI directory.
Here is the code:
def command = "java -jar UpdateAppIdXMLRequest.jar file.xml"
def process = command.execute()
process.waitFor()
def output = process.in.text
log.info output
This jar creates 25 xml files that should be able to be picked up by the SoapUI and put them in as TestSteps in the same project. In my java code in what path do I put these files?
Here is the code in my jar:
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
class UpdateAppIdXMLRequest {
static main(args) {
try {
SAXBuilder builder = new SAXBuilder();
File xmlFile = new File("c:\\file.xml");
Document doc = (Document) builder.build(xmlFile);
Element rootNode = doc.getRootElement();
// Create loop to create 25 testStepApps
for (int i = 1; i < 26; i++) {
// Get current AppID, incrementAppID and update the ApplicationNumber attribute value for next test script.
int appID = Integer.parseInt(rootNode.getAttributeValue("ApplicationNumber"));
appID++;
String appIDValue = Integer.toString(appID);
rootNode.getAttribute("ApplicationNumber").setValue(appIDValue);
XMLOutputter xmlOutput = new XMLOutputter();
// Create new XML file with next AppID
xmlOutput.setFormat(Format.getPrettyFormat());
xmlOutput.output(doc, new FileWriter("c:\\testStepApp" + i + ".xml"));
// xmlOutput.output(doc, System.out);
// System.out.println("File updated!");
}
} catch (IOException io) {
io.printStackTrace();
} catch (JDOMException e) {
e.printStackTrace();
}
}
}
Any help/direction would be appreciated.
Thanks.
In order to do so, I recommend that you use directly groovy test step instead of a jar, this way you have more flexibility an you have not recompile the jar each time you must need to change something.
So, in order to achieve your goal, at first you need to create a TestCase inside your project, create a SOAP Test Step and Groovy Test Step inside like this:
I will use SOAP Test Step to create the other test steps (to create test steps it needs the wsdl:operation and so on, and it's more easy to copy the test step that create directly).
In the Groovy Test Step I will put the necessary code to do the work which is showed below:
import com.eviware.soapui.support.XmlHolder
import com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory
// read your request template
def requestFile = new File("C:/file.xml");
// parse it as xml
def requestXml = new XmlHolder(requestFile.text)
// get the current testCase to add testSteps later
def tc = testRunner.testCase;
// get the testStep as template to create the other requests
def tsTemplate = tc.getTestStepByName("Template");
// loop to create 25 testStep
for(int i = 1; i < 26; i++){
// xpath expression to get applicationNumber attribute in root node
def xpathNodeAttr = "/*/#ApplicationNumber";
// get the root node attribute applicationNumber throught an XPATH
int appId = Integer.parseInt(requestXml.getNodeValue(xpathNodeAttr));
// add 1 to appId
appId++;
// set the value again in the attribute
requestXml.setNodeValue(xpathNodeAttr,appId);
def testStepName = "TestStep_ApplicationNumber_"+ String.valueOf(appId)
log.info testStepName;
log.info testStepName.getClass().getName()
log.info tc.getClass().getName()
// create a new testStepConfig
def testStepFactory = new WsdlTestRequestStepFactory();
def testStepConfig = testStepFactory.createConfig( tsTemplate.getOperation(), testStepName )
// add the new testStep to TestCase
def newTestStep = tc.insertTestStep( testStepConfig, -1 )
// set the request which just create
newTestStep.getTestRequest().setRequestContent(requestXml.getXml())
}
This code it's basically your java code "translated" to groovy and added the necessary code to create the test steps. In a few words this code reads a request from a file, and create 25 test steps in the current test case using the request, in each request it only changes the ApplicationNumber attribute of the root node adding it +1.
EDIT BASED ON COMMENT:
If you use a REST Request step instead of SOAP Request Step you have to change a bit your groovy code to use com.eviware.soapui.impl.wsdl.teststeps.registry.RestRequestStepFactory and getTestRequest() method on it. So if you have a REST Service with a POST method create a Test Case with a REST Request test step and Groovy Test Step like this:
And use this groovy code instead, basically this code it's the same and works like the code above and makes the same thing with REST Request instead of SOAP Request:
import com.eviware.soapui.support.XmlHolder
import com.eviware.soapui.impl.wsdl.teststeps.registry.RestRequestStepFactory
// read your request template
def requestFile = new File("C:/file.xml");
// parse it as xml
def requestXml = new XmlHolder(requestFile.text)
// get the current testCase to add testSteps later
def tc = testRunner.testCase;
// get the testStep as template to create the other requests
def tsTemplate = tc.getTestStepByName("Template");
// loop to create 25 testStep
for(int i = 1; i < 26; i++){
// xpath expression to get applicationNumber attribute in root node
def xpathNodeAttr = "/*/#ApplicationNumber";
// get the root node attribute applicationNumber throught an XPATH
int appId = Integer.parseInt(requestXml.getNodeValue(xpathNodeAttr));
// add 1 to appId
appId++;
// set the value again in the attribute
requestXml.setNodeValue(xpathNodeAttr,appId);
def testStepName = "TestStep_ApplicationNumber_"+ String.valueOf(appId)
log.info testStepName;
log.info testStepName.getClass().getName()
log.info tc.getClass().getName()
// create a new testStepConfig
def testStepFactory = new RestRequestStepFactory();
def testStepConfig = testStepFactory.createConfig( tsTemplate.getTestRequest(), testStepName )
// add the new testStep to TestCase
def newTestStep = tc.insertTestStep( testStepConfig, -1 )
// set the request which just create
newTestStep.getTestRequest().setRequestContent(requestXml.getXml())
}
Hope this helps.

Resources