SoapUI: Using Run TestCase test step doesn't pass in context - groovy

I'm using SoapUI Pro. The SoapUI site says I should be able to set context variables and get those values at any time during the test's execution. In the help it says:
A common usage scenario is for looping or keeping track of progress by saving the corresponding counters and collections to the context and using them to control flow as required.
I can't get this to work if I'm using the out-of-the-box 'Run TestCase' test step. The called test doesn't seem to get passed the context information.
Here's a very basic example. In my originating test I put in some Groovy script that says this:
context.PassedInTest = "Is this passed in?" log.info(context.PassedInTest)
Then in my called test, I have another Groovy script that says this:
log.info(context.PassedInTest)
Upon execution of the test, the log displays: "Is this passed in?" for the originating test, but then the log displays null for the called test:
Mon Oct 27 12:59:45 EDT 2014:INFO:Is this passed in?
Mon Oct 27 12:59:56 EDT 2014:INFO:null
Is there a way to pass in the context if using the out-of-the-box 'Run TestCase' test step in SoapUI (i.e. not using Groovy script)? What am I doing wrong?

I managed to get this working, but I can't say if it's hacky or not...
Consider following project structure:
Project
|-CallingTestSuite
| |-CallingTestCase
| |-TestSteps
| |-LocalScript (Groovy TestStep)
| |-RemoteScript (Run TestCase TestStep)
|-CalledTestsuite
|-CalledTestCase
|-TestSteps
|-CalledScript (Groovy TestStep)
Now, you cannot pass (at least I didn't find a way) context of LocalScript to CalledScript.
You can, however, access the content of LocalScript from CalledScript.
LocalScript:
context.PassedInTest = "Is this passed in?"
log.info(context.PassedInTest)
CalledScript:
if (context.getProperty("#CallingTestRunContext#") != null){
log.info(context.getProperty("#CallingTestRunContext#").PassedInTest);
}
Keep in mind that it needs to be executed as CallingTestSuite, CallingTestCase, or Project, otherwise the target context won't be available.

Related

Jmeter ${__setProperty()} Not working across multiple threads in the same thread group

I am trying to do setproperty across multiple threads in the same threadgroup, the postprocessor set new variable using setproperty, so that It can be accessed across multiple threads.
In Beanshell preprocessor, I'm having below line of code.
${__setProperty("url", "youtube")};
Under thread Group I'm having Beanshell post processor, having below one line in postprocessor.
${__setProperty("url", "google")};
under thread group, I have Http Sampler, in hostname field I have given ${__property(url)}.com
The Aim is, when it executes first time, the URL will be google.com and when first threads complete than
the URL becomes youtube.com
But the setProperty only set google, and the second one in postprocessor was not working.
Refer the below Image for details, it shows how I created the element in Jmeter.
enter image description here
Note: This was just a sample use case, but I have complex example, but answering to this question will help me to add the logic in complex script.
Thanks
So is the goal that the very first thread to complete will change the URL for all subsequently created threads?
My understanding of the documentation is that you can't change the value of a property inside the thread-group:
Properties can be referenced in test plans - see Functions - read a property - but cannot be used for thread-specific values.
(see http://jmeter.apache.org/usermanual/test_plan.html#properties)
My assumption is that each thread in a thread-group gets a copy of the properties. If you change the value of the property inside the thread group, then you are actually changing the copy for that particular thread. Since you are changing it in the post-processor, the thread is very likely just about to be disposed, resulting in your change being lost. After disposal a new thread is created but with the original value of the property.
So what you need to do is figure out how to change the value outside of the thread-group.
I have done something similar in my own tests whereby I am changing the value of a property in the middle of the test, and the value is picked up immediately by all of the active thread-groups, resulting in each new thread created from that point forward getting the new value. I am doing this by using the Beanshell Server: https://jmeter.apache.org/usermanual/best-practices.html#beanshell_server
In my specific case I use jenkins job that calls a shell-script which connects to the beanshell-service running on the local-host:
java -jar ${jmeter_home}/apache-jmeter-5.0/lib/bshclient.jar localhost 9000 ${test_plan_home}/update_Prop.bsh "${property}" "${value}"
where my update_prop.bash file is simply:
import org.apache.jmeter.util.JMeterUtils;
JMeterUtils.getJMeterProperties().setProperty(args[0],args[1]);
You would not need to use Jenkins or anything like that, though - if you setup your JMeter Process to include the Beanshell-server (see the link above) then you can simply replace the code in your post-processor:
${__setProperty("url", "google")};
with the code to connect to the beanshell server and execute that command there instead:
exec("./updateprop.bash url google");
JMeter properties are global therefore once you set the property it is available for all threads
Each JMeter thread (virtual user) executes Samplers. Pre and Post processors are obeying JMeter Scoping Rules Looking into your test plan the execution order is following:
Beanshell PreProcessor
HTTP Request Sampler
Beanshell PostProcessor
therefore HTTP Request sampler will never hit youtube (unless you run into a race condition due to concurrency) because PreProcessor will set the URL back to google
It is recommended to use JSR223 Test Elements and Groovy language for scripting since JMeter 3.1
It is NOT recommended to inline JMeter Functions and/or variables into scripts, you need to either use "Parameters" section or go for code-based equivalents instead so you need to replace this line:
${__setProperty("url", "youtube")};
with this one:
props.put("url", "youtube");

How to print session id in logs by post processor in Jmeter

Can i print session id (which i extract by Regex in previous request response)
in logs by JSR223 postProcessor sampler(groovy)
Thnks in advance
Your variable can be access using vars, for example if you save it as sessionId:
log.info("Session id=" + vars.get("sessionId"));
vars - (JMeterVariables) - gives read/write access to variables
log - (Logger) - can be used to write to the log file
Be aware that it is recommended to use JMeter's built-in test elements and void scripting where possible so I would rather suggest considering using __log() function in order to print the value like:
Double check that your variable is defined and has the value using Debug Sampler and View Results Tree listener combination
Once you verify that the variable is there you can print it to the log using the following syntax:
${__log(${your_variable_reference_name)}
If you still want to proceed with Groovy - the equivalent syntax would be:
log.info(vars.get('id'))
See Top 8 JMeter Java Classes You Should Be Using with Groovy for more information if needed

How to record the timestamp of an XML request in SoapUI and use it in an assertion?

I have a test case in SoapUI NG Pro which has the following steps:
POST REST Request that starts a process
JDBC Request where I check that the process Start Date has been logged to a database table
Delay (to simulate the time it takes for the process to run)
JDBC Request where I check that the End Date and Duration have been logged to the table
I would like to capture the timestamp of the POST Request to use within my assertions in steps 2 and 4.
I have looked around online and some people have mentioned using Events while others have mentioned using a Script TestStep but I haven't been able to get either to work.
I can get the POST Response timestamp but am looking for the Request timestamp in particular. I also noticed that there is a timestamp in the Request Log but again I don't know how to access that.
Any help would be greatly appreciated. Its probably also worth mentioning that I am using JavaScript instead of Groovy.
You can add a Script Assertion for the Soap Request test step and add the below statement in order to show the time taken.
log.info messageExchange.response.timeTaken
If you want the above value to be accessible in other steps, then use below(which stores the value to test case level, so that it is easy to access the test case property in other steps of the same test case):
context.testCase.setPropertyValue('TIME_TAKEN', messageExchange.response.timeTaken.toString())
In the later steps, use Property Expansion to read the test case level property value
def timeTaken = context.expand('${#TestCase#TIME_TAKEN}') as Integer

Selecting endpoints dependent on which level to run tests

I have a bit of structural dilemma in soap. When running tests, it can be possible to run tests at project, test suite or test case level.
Now currently what happens is that we can run a whole project via project level and it will display a prompt box to select an endpoint (through a project level setup script and produces a project report using the project level tear down script).
However, it may be possible that the tester may not want to run a whole project and only wants to run a test suite or even a test case. Now it may be possible that the tester may only want to run only a test suite or even only a test case. Now it would be a hassle disabling suites or cases you don't want to run.
Now the problem i have is that if I start putting prompt boxes to select endpoints at suite or case level, everytime we hit a suite or case, it will always ask for an endpoint. Another thing is that I am thinking not creating suite or test case reposts because if running many suites or cases one by one, it is just an overkill on reporting.
I like your thinking on this, but I was speaking with my professional colleague and what we're thinking is this:
Add the below code for all test suites and test case level in their relevant setup scripts where it asks for endpoint (this is same code used in project set up script for selecting endpoint):
import com.eviware.soapui.support.*
def alert = com.eviware.soapui.support.UISupport
def urls = []
project.properties.each
{
if (it.value.name.startsWith("BASE_URL_"))
{
urls.push(it.value.name.replace("BASE_URL_", ""))
}
}
def urlName = alert.prompt("Please select the environment URL", "Enter URL", urls)
if (urlName)
{
def url = project.getPropertyValue("BASE_URL_" + urlName)
def urlBase = "BASE_URL_" + urlName
project.setPropertyValue("BASE_URL", url)
switch (urlBase){
case "BASE_URL_TEST":
project.setPropertyValue("DOMAIN_NAME", "TEST");
break;
case "BASE_URL_STAGE":
project.setPropertyValue("DOMAIN_NAME", "STAGE");
break;
default:
project.setPropertyValue("DOMAIN_NAME", "NO DOMAIN");
break;
}
}
else
{
log.warn 'haven\'t received user input'
log.warn 'No base URL is selected or cancelled, try again'
assert false
}
Now what we add is the following and we may need to use properties but again see what you think is best:
If test is ran at project level, it will prompt to select endpoint through project setup script but it will not ask for selecting endpoint through test suite or test case setup script. So it's only a single endpoint selection
If test is ran at suite level, it will prompt to select endpoint through project setup script but it will not ask for selecting endpoint through test case setup script. So it's only a single endpoint selection
For running at test case level, well it only runs for that test case so it's at the lowest level as it asks for an endpoint for that test case.
We can't have setup scripts disabled at any level because there maybe over code in those setup script that will need to be exectued, we just need a way to say depending on which level, don't ask for selecting endpoints at lower levels.
Seems complicated to implement but does anyone know best way to implement this or do they even have a better idea than this theory?
Thanks
For a moment, let us assume you get it done for all levels (project, suite, and each case). May be you forgot about the step level ;-)
Do you have any Pros in your approach?, for me, NO.
Cons in your approach:
Each time user executes a test (be it project / suite / any test case), engineer needs to select value from the drop down, which is unwanted though testing against the same server as previous test case & little annoying.
Test execution requires manual intervention each time test execution is invoked.
User Interface is required as drop down being used.
Will be come road block / hurdle for end to end automation or to achieve automation.
Test execution can't done in headless mode. And this is important if you need to use Continuous Integration tools.
Proposed Approach :-
If I have to do the above, I would do the following. That would be clean, damn simple, no such complications would arise that you had mentioned in the long summary.
Looks there are following project properties defined with addresses of the test servers:
BASE_URL_TEST
BASE_URL_STAGE
There is also another project property defined BASE_URL and all the above logic is to allow the user to select the value from above properties to base URL value.
Now all user have to do is change the value for project property BASE_URL. I would think just user have to set one of the below value by hand what he / she needed as (one of them) before proceeding with their tests.
${#Project#BASE_URL_TEST} or
${#Project#BASE_URL_STAGE}
NOTE that a property value can be referred into another property by the use of Property Expansion like above.
With the above, user can set whatever is needed and change only if required or have to change the test server.
No setup script at any level is required any more, and just simply change the value of the property.
Properties are given to make to life simple, which can be used in N number of places and maintain the project easily.
Most Importantly, overcome the Cons mentioned in the beginning.
It is general practice that SoapUI is used to design the tests, and SOAPUI_HOME/bin/testrunner.bat or .sh utility to execute the tests in command line mode and that is the way to achieve Continuous Integration.
That's why use of properties helps here to achieve the above without any issues.
Even simple:
Just have one project property BASE_URL (remove others), user have to just edit the property value and have the test server name / IP address and is done for once, say http://testjuniper. Isn't it dead simple?
And I believe, the engineer would definitely know which server he / she is using to execute the tests.
Having said that, now user do not have to bother at all, irrespective of executing a project / suite / test case, as long as testing is carried out against the same server / environment.
Once, the test execution is finished against TEST environment, the engineer may move on to other environment say STAGING, just change BASE_URL property value accordingly.

How to use nested paramter in SoapUI context.expand expression?

My use case is that I want to do a bulk update of request bodies in multiple SoapUI projects.
Example of request body.
{
"name": "${#TestSuite#NameProperty}"
"id": "${#TestSuite#IdProperty}"
}
I want to expand the property ${#TestSuite#NameProperty} through Groovy, and get the value stored at TestSuite level then modify it as necessary.
Suppose I have 50 test steps in my test case and I want to expand the request for each one from Groovy script. To expand a specific test steps, I would pass the test Steps's name. For example:
expandedProperty = context.expand('${testStep1#Request}')
But, how can I achieve the same, if I wanted to iterate over all 50 test steps? I tried to used a nested parameter inside the context.expand expression, but it did not work. For Example:
currentTestStepName = "TestStep1"
expandedProperty = context.expand('${${currentTestStepName}#Request}')
This only returned me the expanded request from the test step right above it (where I am running the groovy script from) rather than the "TestStep1" step. ( Which is madness!)
Also, context.expand only seems to work while executing via Groovy script from the SoapUI workspace project. Is there and other way, or method similar to context.expand which can expand the properties like "${#TestSuite#NameProperty}" during headless execution? Eg: A groovy compiled jar file imported in SoapUI.
Thanks for any help in advance!
You can use context.expand('${${currentTestStepName}#Request}') way to get it.
There are other approaches as well, which does not use context.expand.
In order to get single test step request of any given test step:
Here user passes step name to the variable stepName.
log.info context.testCase.testSteps[stepName].getPropertyValue('Request')
If you want to get all the requests of the test case, here is the simple way using the below script.
/**
* This script loops thru the tests steps of SOAP Request steps,
* Adds the step name, and request to a map.
* So, that one can query the map to get the request using step name any time later.
*/
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep
def requestsMap = [:]
context.testCase.testStepList.each { step ->
log.info "Looking into soap request step: ${step.name}"
if (step instanceof WsdlTestRequestStep) {
log.info "Found a request step of required type "
requestsMap[step.name] = context.expand(step.getPropertyValue('Request'))
}
}
log.info requestsMap['TestStep1']
Update :
If the step that you are interested is REST step, use below condition instead of WsdlTestRequestStep in the above.
if (step instanceof com.eviware.soapui.impl.wsdl.teststeps.RestTestRequestStep) { //do the stuff }

Resources