JMeter JSR223 script - how do we load multiple groovy files? - groovy

I want to structure my code well to use in JMeter, so I plan to have the groovy scripts in separate groovy files. The JSR223 script can load the script file and execute it. So far so good.
The groovy scripts will get complex, so I want to split the files into multiple files and use them. For e.g. have a file utils.groovy with content
public class SharedStore {
// Initializes the store to store all information about all the data being worked upon
def initializeStore() {
HashMap allStore = new HashMap();
// def props = org.apache.jmeter.util.JMeterUtils.getJMeterProperties()
props.put("${storeHashSet}", allStore);
}
This class will grow with more methods and will be used by all scripts invoked from JMeter. There will be other similar classes.
One JMeter sampler called InitializeEnvironment.groovy wants to use this method thus:
evaluate(new File("<<path>>/Utils.groovy")); // This line is an attempt to include utils.groovy
var shr = new SharedStore().initializeStore();
The above attempt to include groovy file is based on this discussion - https://stackoverflow.com/a/9154553 (and I have tried other options from that discussion to no avail)
Using the above option throws the error:
Script70.groovy: 2: unable to resolve class SharedStore
# line 2, column 11.
var shr = new SharedStore().initializeStore();
I would prefer to wrap things utility methods a class but I can live with having keep methods in global space.
If I do manage to load the additional file like in this - https://stackoverflow.com/a/15904699 - suggestion, without using "class" and have groovy wrap it up in a class for me, props are not available in utils.groovy unless I include the line def props = org.apache.jmeter.util.JMeterUtils.getJMeterProperties() and even then ${storeHashSet} cannot be used
No such property: storeHashSet for class: SharedStore
I want to be able to decompose the scripts into more manageable files, and be able to access JMeter variables and structures in these files.
Any advise on how I can do that in JMeter?

The most straightforward solution would be:
Compiling these individual files into classes
Packaging them into .jar (this one you can skip)
Putting the .jar or (class files if you skipped step 2) in the JMeter Classpath
Using the .jar file in your JSR223 Test Elements

Related

How to share class object between groovy test steps?

I have an external groovy file containing all common functions required to automate my web service testing. I reference those common functions by creating an instance of the Class defined within the external file. Now I have a situation to create an instance of the Class in first groovy test step and to use the same instance in other groovy test steps within my test case.
import groovy.lang.Binding
import groovy.util.GroovyScriptEngine
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
// location of script file is relative to SOAPUI project file.
String scriptPath = groovyUtils.projectPath + "\\Scripts\\"
// Create Groovy Script Engine to run the script.
GroovyScriptEngine gse = new GroovyScriptEngine(scriptPath)
// Load the Groovy Script file
externalScript = gse.loadScriptByName("CustomerQuotes.groovy")
def cq = externalScript.newInstance(context: context, log: log, testRunner: testRunner)
How do I achieve this? I need the reference of cq object in other groovy test steps to call the remaining common functions available within my external grooy file? Please help.
As per your question, the mentioned groovy script test step is placed in an arbitrary test case though it is not a natural fit.
The natural fit for the above script is to use Load Script which is at project level.
In the script, which is mentioned in the question, change below statements
From:
def groovyUtils = new com.eviware.soapui.support.GroovyUtils(context)
// location of script file is relative to SOAPUI project file.
String scriptPath = groovyUtils.projectPath + "\\Scripts\\"
To:
def projectPath = new File(project.path).parent.toString()
String scriptPath = "${projectPath}/Scripts"
And keep the rest of the script same.
Place the script (after the above change) at Project level's Load Script.
Remove the groovy script test step from wherever it is because of the above mentioned reason.
At the end of the script, add the below statement. Basically using the groovy's Meta Programming feature to store the object for sharing.
project.metaClass.myObject = cq
Next step: How to re-use the object (which is the main question)?
Since, your object cq is added to project object, the same can be accessed in any of the groovy script test steps (at any suite or case) using below statement:
def cq = context.testCase.testSuite.project.myObject
//Now call the other methods using cq.method(arguments)
EDIT: It appears that the above solution works for simple/Primitive data types.
However, you have a class instance. For that some more changes are required.
Here is your complete Project level Load Script (includes your code snippet)
def projectPath = new File(project.path).parent.toString()
String scriptPath = "${projectPath}/Scripts"
GroovyScriptEngine gse = new GroovyScriptEngine(scriptPath)
def externalScript = gse.loadScriptByName("CustomerQuotes.groovy")
project.metaClass.myObject {
externalScript.newInstance(context: it, log: log, testRunner: it.testRunner)
}
And the script for Groovy Script test step in different test cases is as follows i.e., just calling the methods of your CustomerQuotes.groovy class.
def obj = context.testCase.testSuite.project.myObject(context)
obj.run()
Assuming that there is a method in the groovy file called run. Of course, you can use your own method.
EDIT 2:
There is another alternative approach too. You need to compile the groovy classes, create jar, copy it under SOAPUI_HOME/bin/ext directory. Of course, soapui tool needs to be restarted after that.
Now you can create instance and make the desired call to the methods as needed in any of the groovy script test steps.

Output resources using Groovy ASTTransformer

I've written a number of Java annotation processors that write some arbitrary data to text files that will be included in my class directory / jar file. I typically use code that looks like this:
final OutputStream out = processingEnv
.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "myFile")
.openOutputStream();
I'm trying to do something similar in a groovy ASTTransformation. I've tried adding a new source file but that (expectedly) must be valid groovy. How do I write arbitrary resources from an ASTTransformation? Is it even possible?
As part of implementing your ASTTransformation, you need to implement the void visit(ASTNode[] nodes, SourceUnit source) method. In it you can call source.getConfiguration().getTargetDirectory() and it will return your build output directory, e.g. /Users/skissane/my-groovy-project/build/classes/groovy/main). You can then write your resources into there, and whatever is packaging them into the JAR (such as Gradle) should pull them from that.
In my case, I wanted to delay writing the resources until OUTPUT phase – since I was creating META-INF/services files, and I wanted to wait until I'd seen all the annotated classes before writing them, or else I'd be repeatedly adding to them for each annotated class – so I also implemented CompilationUnitAware, and then in my setCompilationUnit method I call unit.addPhaseOperation() and pass it a method reference to run during OUTPUT. Note, if you are using a local ASTTransformation, setCompilationUnit will be called multiple times (each time on a new instance of your transformation class); to avoid adding the phase operation repeatedly, I used a map in a static field to track if I'd seen this CompilationUnit before or not. My addPhaseOperation method is called once per an output class, so I used a boolean field to make sure I only wrote the resource files out once.
Doing this caused a warning to be printed:
> Task :compileGroovy
warning: Implicitly compiled files were not subject to annotation processing.
Use -implicit to specify a policy for implicit compilation.
1 warning
Adding this to build.gradle made the warning go away:
compileGroovy {
options.compilerArgs += ['-implicit:none']
}

Creating script library in SoapUI free version

I am new in SoapUI and groovy scripting
I would like to create a repository of groovy scripts that can be reused at various test steps
I am using SoapUI Free version and following is the structure of my SoapUI Project
Project
|-TestSuite
| |-TestCase
| |-TestSteps
| |-LocalScript (Groovy TestStep to reuse library scripts)
| |-OtherTestStep (Run TestCase TestStep)
|-ScriptLibrary
|-TestCase
|-TestSteps
|-GroovyScriptStep1 (Contain a class for commonly used functions)
|-GroovyScriptStep2 (Contain another class for other functions)
Here is what I was able to do:
I was able to create a library using the sample mentioned in this post. Similar to example in the post, my code in test step (GroovyScriptStep1 as per above structure) of library was just reading some value from external file and is used in test step of other TestSuite (LocalScript step in above structure).
Here is the problem:
Now I want to create a new class and add a function to it which will need info from running class and simply print it. The difference here is that some values are generated in the test run and should be passed to library script inorder to process/print etc.
To make my question more clear following is the code snippet
I will be using a simple scenario here
Sample objective: Want to be able to print all the assertions and status (since this will be used in all the test cases I want to create a library)
Code for same when not using library will be as under(this can go as groovy script step)
def obj = context.testCase.getTestStepByName("Request 1");
def assertions = obj.getAssertionList()
//Loop on assertions
assertions.each{
log.info(it.name + ' --> ' + it.status)
Code something similar in Library TestSuite's Test case step
context.setProperty("Assertions", new Assertions());
class Assertions{
def printAssertion(def someArgumentToGetAssertionlistforTestStepinAnotherTestSuite){
def obj = ????
def assertions = obj.getAssertionList()
//Loop on assertions
assertions.each{
log.info(it.name + ' --> ' + it.status)
}
}
}
Code from where I want to call this method (LocalScript as per above project structure)
scripts = testRunner.testCase.testSuite.project.testSuites["ScriptLibrary"];
scripts.testCases["Scripts"].testSteps["Assertions"].run(testRunner, context);
context.Assertions.printAssertion(ArgumentRequired);
This is just one example, I want to create libraries of some more common scripts that use context variable when used locally
Kindly help me with this and please let me know if some more information/clarification is required
What I get from your questions is that you want to create a code library in SoapUI that can be reused.
I think the best way is by creating jar files and deploying it in ext folder of SoapUI
Create a new groovy script file with a class (follows java standards in file naming i.e. class name and file name should be same)
Compile the groovy code file
Create the jar file
Deploy the jar file at SoapUI_Home/bin/ext folder
Restart the SoapUI if was already open
Create the object of class and use the methods anywhere in the SoapUI projects
Note: If you are migrating your project to some other machine, make sure to migrate these libraries as well if you are using them in projects
Details with example:
Step 1: Create a new groovy script file with a class structure
i. Considering the class ScriptLibrary that contain a method named printTestDetails as in following code in it:
class ScriptLibrary {
def context
def testRunner
def log
def printTestDetails(def PrintThisToo) {
log.info 'Name of the test case is :'+testRunner.testCase.name
log.info 'Name of the test suite is : '+testRunner.testCase.testSuite.name
log.info PrintThisToo
}
}
ii. Save the file with class name, ScriptLibrary.groovy in this case
Step 2: Compile the code
i. Open command prompt and fire following command (from the folder where where your .groovy file is kept)
Compile the code:
groovyc -d classes ScriptLibrary.groovy
Step 3: Create the jar file
i. After compiling the code we can create the jar
Create jar file:
jar cvf ScriptLibrary.jar -C classes .
Step 4: Deploy the jar file at SoapUI_Home/bin/ext folder
Step 5: Restart the SoapUI if was already open
Step 6: Create the object of class and use the methods anywhere in the SoapUI projects
i. Creating object
def scripts = new ScriptLibrary(context:context, log:log, testRunner:testRunner)
ii. Calling methods
scripts.printTestDetails(“This is my argument”)
I hope this solves your problem over all, this approach will allow you to freely use the code any where in the SoapUI and most importantly will solve your problem for getting context, log and testrunner in external code
You can also use any IDE of your choice to create code library and work on same to compile and create jar as well.
Let me know if you have any doubts or need more clarification
While I like Samarth's Answer for large projects, there is something about not having to leave SoapUI that makes TestHat's Answer very interesting.
This is what I used in my latest project:
A Testsuite ScriptLibrary with Testcases according to areas and Groovy script steps for every function.
I run these functions with testRunner.testCase.testSuite.project.testSuites["ScriptLibrary"].testCases["Demo"].testSteps["SomeFunction"].run(testRunner, context);, which then mutates the context to include the needed functions.
In order to use the correct log and testRunner instances, I pass those via parameters. e.g. context.someFunction(log, testRunner);
Because normal functions can't be assigned to the context, I either use closures or classes.
For classes I use something like
class SomeClass {
def logger;
def runner;
SomeClass(logger, runner) {
this.runner = runner;
this.logger = logger;
}
public static SomeClass with(logger, runner) {
return new SomeClass(logger, runner);
}
public void someFunction(String a) {
// This prints to the log of the caller function and not this one
logger.info a;
}
}
context.SomeClass = SomeClass;
and then call it with
testRunner.testCase.testSuite.project.testSuites["ScriptLibrary"].testCases["Demo"].testSteps["SomeClass"].run(testRunner, context);
context.SomeClass.with(log, testRunner).someFunction(
"Hello World"
);
This is mainly done because you can't easily create a new instance from the context.SomeClass variable.
While the context is persisted inside a Testcase, it gets cleared when using a "Run Testcase" teststep, which freqently use.
Maybe this was some help to somebody in the future.
This should do it
context.setProperty("Assertions", new Assertions());
class Assertions{
def printAssertion( tStep){
def assertions = tStep.getAssertionList()
//Loop on assertions
assertions.each{
log.info(it.name + ' --> ' + it.status)
}
}
}
and call it like this
TestStep=testRunner.testCase.testSuite.getTestCaseByName("yourTestCase").getTestStepByName("stepName")
context.Assertions.printAssertion(TestStep)
On the project level, you can write "Load Script" and you can save your utility class instance in the project level context:
context.setProperty("ScriptLibrary", new ScriptLibrary())
and in your test suit (eg. in "Setup Script") you can get it:
testSuite.project.context.getProperty('ScriptLibrary')
or in your test case:
testCase.testSuite.project.context.getProperty('ScriptLibrary')
or in your Groovy test step:
testRunner.testCase.testSuite.project.context.getProperty('ScriptLibrary')
For Assertion :
Put this script in Repository
context.setProperty("Assertions", new Assertions());
class Assertions{
def printAssertion(tStep){
def assertions = tStep.getAssertionList()
//Loop on assertions
}
}
Use this Script in SoapUI:
TestStep=testRunner.testCase.testSuite.getTestCaseByName("addTestCase").getTestStepByName("add")
//context.Assertions.printAssertion(TestStep)
scripts = testRunner.testCase.testSuite.project.testSuites["ScriptLibrary"];
scripts.testCases["Demo"].testSteps["TestAssertion"].run(testRunner, context);
context.Assertions.printAssertion(TestStep).each{
log.info(it.name + ' --> ' + it.status)
}
return null

GroovyClassLoader Hierarchy and CompilerConfiguration

Problem:
Create GroovyClassLoader GCLA with explicitly set CompilerConfiguration
Create another GroovyClassLoader GCLB with different CompilerConfiguration that sets the BaseScriptClass and uses GCLA as parent
-> a script loaded in GCLB will not have the BaseScriptClass set correctly (but uses the potentially set basescript of GCLA)
Long Story:
My application uses four Groovy scripts to allow customization of certain actions. Each script provides a small DSL, where the DLSs are different for each script. When the action is excecuted in the application, the corresponding script will be called. Additionally some common groovy scripts can be loaded at application startup for storing of common data or definition of helper functions.
The scripts are heavily typechecked at the start of the application.
My first approach was to have one GroovyClassLoader which first loads the common groovy scripts and then does a loadClass for each of the scripts. The DSL is created by simply adding corresponding elements to the binding of each script befor the script is run. The GroovyClassLoader uses a CompilerConfiguration with TypeCheck-Extensions to typecheck the scripts.
This works quite well but has two severe limitations:
the typechecking has to be performed based on the name of the script (because the DSLs for each script is different)
the typechecking script gets quite complicated because it has to handle all four DSLs
To get rid of the named limitations I tried to use a distinct GroovyClassLoader (with my actual ClassLoader as parent) for each of the script (i. e. one per DSL) and set a corresponding ScriptBaseClass which provides the functionality for the DSL. This works very well: the typechecking code is reduced drastically, I can have distinct typechecking scripts for each DSL and I don't have to mess with the name of the script.
But I don't get this to work with the common groovy scripts. As soon as I try to use the GroovyClassLoader of the common scripts as parent for the GroovyClassLoader of the DSL scripts, the DSL no longer gets the correct BaseScriptClass although it's explictly set in the CompilerConfiguration. It seems that the child GroovyClassLoader uses the BaseScriptClass of the parent GroovyClassLoader.
Any ideas of how to get this working?
UPDATE
In my original post I did not mention that I'm using scala. Actually I tried the following code snippet:
val rootConfig = {
val cf = new CompilerConfiguration(CompilerConfiguration.DEFAULT)
cf.setSourceEncoding("UTF-8")
cf.addCompilationCustomizers(new ASTTransformationCustomizer(classOf[CompileStatic]))
cf
}
val childConfig = {
val cfg = new CompilerConfiguration(CompilerConfiguration.DEFAULT)
cfg.setSourceEncoding("UTF-8")
cfg.setScriptBaseClass("NonExisting")
cfg.addCompilationCustomizers(new ASTTransformationCustomizer(classOf[CompileStatic]))
cfg
}
val rootGCL = new GroovyClassLoader(getClass.getClassLoader, rootConfig, false)
val childGCL = new GroovyClassLoader(rootGCL, childConfig, false)
This does not work and the child ClassLoader seems to ignore the CompilerConfiguration completely: it does not complain about the non-exiting BaseScriptClass and if I switch off the static-compilation in the rootConfig it will not even perform the static checks.
If I move the ClassLoader creation to a Java file everything works as expected
GroovyClassLoader has several constructor. For example one for ClassLoader and one for GroovyClassLoader. The GroovyClassLoader version will take the configuration from it. But if you do for example new GroovyClassLoader((ClassLoader) parent) or go with giving the configuration yourself. And for that it is even enough to give null as configuration. The constructor logic will take CompilerConfiguration.DEFAULT then to configure the GroovyClassLoader: new GroovyClassLoader(parent,null) and if you want to be very sure no strange paths are added to the GroovyClassLoader, use the variant with a boolean new GroovyClassLoader(parent,null,false)

What is the best way to import constants into a groovy script?

I have been setting up a scripting envrionment using Groovy. I have a groovy script called FrameworkiDatabase.groovy which contains a class of the same name. This works fine. I also have another file called connections.groovy which contains maps like the following:
SUPPORT2=[
host:"host.name",
port:"1521",
db:"support2",
username:"username",
password:"password",
dbType:"oracle"
]
This holds a collection of database bookmarks, a bit like an oracle tnsnames file, so I don't need to remember all the parameters when connecting to databases.
When using groovysh, I can import this using the load command, and it is available in current scope. How can I load it as part of a script the same way? It has no class definition around it - does it need one? I have tried doing that, and adding a static import, but that didn't work...
I tried something like this, but no luck:
testFrameworkiDatabase.groovy:
import static connections
def db = new FrameworkiDatabase(SUPPORT2)
db.listInvalidObjects()
db.getDBSchemaVersion()
db.getFWiVersion()
db.getSPVersion()
db.getFileloaderVersion()
db.getAdminToolVersion()
db.getReportsVersion()
So I want to load those connections as constants - is there any way I can do this easily?
Not sure if it's the best way, but one way would be to write this into Connections.groovy
class Connections {
static SUPPORT2 = [
host:"host.name",
port:"1521",
db:"support2",
username:"username",
password:"password",
dbType:"oracle"
]
}
Then, compile this with groovyc Connections.groovy to generate a class file
Then, in your test script or on the groovysh prompt, you can do:
import static Connections.*
println SUPPORT2
To get the output:
[host:host.name, port:1521, db:support2, username:username, password:password, dbType:oracle]
If compiling the Connections.groovy class isn't good enough, I think you're going to be looking at loading the source into a Binding object by using one of the Groovy embedding techniques

Resources