Coder Social home page Coder Social logo

smicyk / groovy-jmeter Goto Github PK

View Code? Open in Web Editor NEW
13.0 2.0 1.0 1007 KB

A Groovy-based DSL for building and running JMeter test plans from command line and more.

License: Apache License 2.0

Shell 0.24% Groovy 99.52% Batchfile 0.24%
groovy groovy-script jmeter dsl command-line scripts monitoring performance testing

groovy-jmeter's Introduction

Groovy JMeter DSL

The Groovy-JMeter project is simple DSL to write JMeter test plans.

Build & Test

It has following features:

  • keep JMeter test files (*.jmx) as a code (Groovy scripts)
  • run scripts as standalone scripts, JUnit tests or Gradle tasks
  • support of basic JMeter elements like controllers, groups, extractors and assertions
  • support of HTTP, JSR223 and JDBC JMeter samplers
  • support of JMeter listeners (includes a listener with backed systems like influxdb)
  • add modularization of the script with insert keyword (can insert part of the other test script)

Current version uses Apache JMeter 5.6.3 as runtime engine.

Note, that you don't have to download any components of JMeter to run the scripts, all necessary components are initialized at startup.

Checkout the project wiki for quick reference of all keywords and properties

Check Component Status page for supported JMeter features

Prerequisites

Before start, you should have:

Or you can use the Docker approach (check the section below):

How to start

Starting from version 0.11.0, all artifacts are available through maven repository. If you want the latest changes, go to Building from source.

First steps

To run your test script, it is enough to type in your favorite editor the following lines:

@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.DefaultTestScript script

start {
    plan {
        group {
            http 'GET http://www.example.com'
        }

        // optional element, shows execution progress
        summary(file: 'log.jtl')
    }
}

This test plan will start one user and make call to http://www.example.com

Please remember that first execution of the script can take some time as Ivy must download all dependency to local cache

To run the script, execute the following command:

groovy yourscriptname.groovy

If you want to start the same script inside a docker, execute commands below:

# one time setup for script dependencies, it will speed up future executions
docker volume create --name grapes-cache

# in Linux
docker run --rm -u groovy -v "$(pwd)":"/home/groovy" -v "grapes-cache":"/home/groovy/.groovy/grapes" groovy:3.0.20-jdk11 groovy yourscriptname.groovy

# in Windows
docker run --rm -u groovy -v %CD%:"/home/groovy" -v "grapes-cache":"/home/groovy/.groovy/grapes" groovy:3.0.20-jdk11 groovy yourscriptname.groovy

Typical output on console should look like this:

+      1 in 00:00:01 =    1,7/s Avg:   419 Min:   419 Max:   419 Err:     0 (0,00%) Active: 1 Started: 1 Finished: 0
=      1 in 00:00:01 =    1,7/s Avg:   419 Min:   419 Max:   419 Err:     0 (0,00%)

Test plan generator

There is *.har converter to generate scripts from *.har files. It can greatly speed up scripts generation for your tests.

DefaultTestScript vs. TestScript

There are two implementation for the test scripts, the DefaultTestScript and TestScript. The DefaultTestScript is very basic and doesn't have any additional features. The TestScript comes with additional command line support.

When you change the line with @groovy.transform.BaseScript annotation to net.simonix.dsl.jmeter.TestScript, so your file would look like this:

@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script

start {
    plan {
        group {
            http 'GET http://www.example.com'
        }

        // optional element, shows execution progress
        summary(file: 'log.jtl')
    }
}

You can execute your script with help parameter to see all available options:

groovy yourscriptname.groovy --help
Usage: groovy [-h] [--no-run] [--jmx-out=<file>] [-V=<variable=value>
              [=<variable=value>]...]...
  -h, --help             Show help message
      --jmx-out=<file>   Generate .jmx file based on the script
      --no-run           Execute the script but don't run the test
  -V, --vars=<variable=value>[=<variable=value>]...
                         Define values for placeholders in the script

Some interesting usage might be to generate .jmx file with additional variables which comes from environment:

@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script

start {
    plan {
        // HTTP defaults test element
        defaults protocol: 'http', domain: "${var_host}", port: var_port

        group (users: 10) {
            http 'GET /books'
        }

        // optional element, shows execution progress
        summary(file: 'log.jtl')
    }
}

To generate .jmx file from this script you can execute the following in the command line:

groovy yourscriptname.groovy --jmx-out yourscriptname.jmx --no-run -Vvar_host=localhost -Vvar_port=8080

For more examples you should check the examples folder and unit tests.

To get more information about all available options you should check groovy docs page or generate it from the source.

gradlew groovydoc

cd ./build/docs/groovydoc && index.html

More advanced example

The example below shows more constructs related to testing simple REST API.

@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script

start {
    // define basic test plan
    plan {
        // add user define variables (this will be defined on Test Plan not as separate User Defined Variables test element)
        arguments {
            argument(name: 'var_host', value: 'prod.mycompany.com')
        }

        // define HTTP default values
        defaults(protocol: 'http', domain: '${var_host}', port: 1080)

        // define group of 1000 users with ramp up period 300 seconds
        group(users: 1000, rampUp: 300) {
            // add cookie manager for HTTP requests
            cookies(name: 'cookies manager')

            // load users data from file into variables
            csv(file: 'users.csv', variables: ['var_username', 'var_password'], delimiter: ';')

            // define POST request with parameters and extract tracking id for later use
            http('POST /login') {
                params {
                    param(name: 'username', value: '${var_username}')
                    param(name: 'password', value: '${var_password}')
                }

                extract_regex expression: '"trackId", content="([0-9]+)"', variable: 'var_trackId'
            }

            // define GET request and extract data from json response
            http('GET /api/books') {
                params values: [ limit: '10' ]

                extract_json expressions: '$..id', variables: 'var_bookId'
            }

            http('GET /api/books/${var_bookId}') {
                extract_json expressions: '$..author.id', variables: 'var_authorId'
            }

            // simplified form of HTTP request, no parenthesis
            http 'GET /api/authors/${var_authorId}'

            // define simple controller to make POST request
            simple {
                headers values: [ 'Content-Type': 'application/json' ]

                http('POST /api/books/${var_bookId}/comments') {
                    body '''\
                        {
                            "title": "Title",
                            "content": "Comment content"
                        }
                    '''

                    // check assertion about HTTP status code in the response
                    check_response {
                        status() eq 200
                    }
                }
            }
        }

        // output to .jtl file
        summary(file: 'script.jtl', enabled: true)
    }
}

The next example shows testing database with JMeter.

@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')
@Grab('org.postgresql:postgresql:42.2.20') // download JDBC driver for your database

@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script

start {
    plan {
        before {
            jdbc datasource: 'postgres', {
                pool connections: 10, wait: 1000, eviction: 60000, autocommit: true, isolation: 'DEFAULT', preinit: true
                connection url: 'jdbc:postgresql://database-db:5432/', driver: 'org.postgresql.Driver', username: 'postgres', password: 'postgres'
                init(['SET search_path TO public'])
                validation idle: true, timeout: 5000, query: '''SELECT 1'''
            }

            jdbc use: 'postgres', {
                jdbc_preprocessor use: 'postgres', {
                    execute('''
                        DROP TABLE employee
                    ''')
                }

                execute('''
                    CREATE TABLE employee (id INTEGER PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50), email VARCHAR(50), salary INTEGER)
                ''')
            }
        }

        group users: 100, rampUp: 60, loops: 1, {
            csv file: 'employees.csv', delimiter: ',', ignoreFirstLine: true, variables: ['var_id', 'var_first_name', 'var_last_name', 'var_email', 'var_salary']

            jdbc use: 'postgres', {
                execute('''
                    INSERT INTO employee (id, first_name, last_name, email, salary) VALUES(?, ?, ?, ?, ?)
                ''') {
                    params {
                        param value: '${var_id}', type: 'INTEGER'
                        param value: '${var_first_name}', type: 'VARCHAR'
                        param value: '${var_last_name}', type: 'VARCHAR'
                        param value: '${var_email}', type: 'VARCHAR'
                        param value: '${var_salary}', type: 'INTEGER'
                    }
                }
            }

            jdbc use: 'postgres', {
                query(limit: 1, result: 'var_employee_count', inline: '''
                    SELECT count(*) FROM employee
                ''')

                jsrpostprocessor(inline: '''
                    log.info('Number of employees: ' + vars.get('var_employee_count'))
                ''')
            }

            // output to .jtl file
            summary(file: 'script.jtl', enabled: true)
        }
    }
}

Building from source

Clone, build and publish jars to your local repository:

git clone https://github.com/smicyk/groovy-jmeter.git

# in Linux
./gradlew clean build publishIvyPublicationToIvyRepository

# in Windows
gradlew clean build publishIvyPublicationToIvyRepository

You can also try alternative approach and build everything on Docker without installing anything on your machine:

git clone https://github.com/smicyk/groovy-jmeter.git

docker volume create --name grapes-cache

# in Linux
docker run --rm -u gradle -w "/home/gradle/groovy-jmeter" -v "$(pwd)":"/home/gradle/groovy-jmeter" -v "grapes-cache":"/home/gradle/.groovy/grapes" gradle:8.5-jdk11 gradle -Dorg.gradle.project.buildDir=/tmp/gradle-build clean build publishIvyPublicationToIvyRepository

# in Windows
docker run --rm -u gradle -w "/home/gradle/groovy-jmeter" -v "%CD%":"/home/gradle/groovy-jmeter" -v "grapes-cache":"/home/gradle/.groovy/grapes" gradle:8.5-jdk11 gradle -Dorg.gradle.project.buildDir=/tmp/gradle-build clean build publishIvyPublicationToIvyRepository

Conventions

There are several conventions used in the design of the DSL.

Naming

All names in the DSL should make the script easy to read and concise. Most of the keywords and properties names are single words. The examples of such keywords might be: plan, group, loops. A similar rule applies to properties, e.g. name, comments, forever. However, some keywords must have two words like execute_if, extract_regx, assert_json. The longer names for properties are in camel case, e.g. rampUp, perUser.

Users vs. Threads

In JMeter world, the users and threads are used interchangeably (both means virtual concurrent users executing test plan). In the script, we use the users as a convention. Check the example below:

start {
    plan {
        group users: 10, {
            // define random variable 'var_random' for each user (in other words each user has its own random generator)
            random(minimum: 0, maximum: 100, variable: 'var_random', perUser: true)
        }
    }
}

Default values

All keywords in the DSL has predefined default values for its properties. For example, each keyword has name property with a default value defined. If there is no name property given for the keyword, the script will use the default value. You can check default values in Groovy docs. Below are more examples:

start {
    // would be same as plan (name: 'Test Plan')
    plan { 

    }

    plan {
        // would be same as group (users: 1, rampUp: 1) 
        group {

        }
    }
}

Required properties

Even though each property has a default value, sometimes there is no sense to have a test element without specific property value. Such properties are required and raise an exception if missing. Check the example below:

Please note that in JMeter documentation there are many properties which are required. Still, in the DSL we make them only required if they vital to execution, otherwise they have some reasonable default value.

start {
    plan {
        group {
            // condition property is required, otherwise using execute_if controller has no sense
            execute_if (condition: '''__jexl3(vars.get('var_random') mod 2 == 0)''') {

            }
        }
    }
}

Groovy as DSL

There are several things to keep in mind while writing the scripts; most of the stuff relates to Groovy language:

  • using different quotes around string, please refer to Groovy docs about string and quotes and check example below:
// test_1.groovy
start {
    // using single quotes (for Java plain String)
    plan(name: 'Test name')
}

// test_2.groovy
start {
    // using single quotes is recommended in most situation (should be used when you want use JMeter variable substitution in the script)
    plan(name: '${var_variable}')
}

// test_3.groovy
start {
    // using double quotes (for GString, interpolation available during test build but not execution by JMeter engine)
    plan (name: "${var_param}")
}
  • there are several ways to use keywords, check the example below:
// test_1.groovy
start {
    // keyword without properties, after keyword you can open closure without any properties or parenthesis
    plan {

    }
}

// test_2.groovy
start {
    // keyword with properties, the properties of the keyword can have properties defined as key/value pair 
    plan(name: 'test', comments: 'new test plan') {

    }
}

// test_3.groovy
start {
    // keyword with properties but without parenthesis, please note that after all properties you must put comma
    plan name: 'test', comments: 'new test plan', {

    }
}

// test_4.groovy
start {
    // keyword without properties and child test elements, note that the parenthesis must be used
    plan()
}

Shortcuts

There are many places where we can use shortcuts to define the same thing:

  • the first argument for the keyword can be simple value. In most cases it is treated as a name for test element:
// test_1.groovy
start {
    // long version
    plan name: 'Test plan111'
}

// test_2.groovy
start {
    // short version
    plan 'Test plan222'
}

// test_3.groovy
start {
    // short version with properties
    plan 'Test plan', enabled: true
}
  • some keywords treat first value as shortcut
start {
    plan {
        group {
            // long version
            loop count: 10
            // short version
            loop 10

            // long version
            execute_if condition: '''__jexl3(vars.get('var_random') mod 2 == 0)'''
            // short version
            execute_if '''__jexl3(vars.get('var_random') mod 2 == 0)'''
        }
    }
}
  • there is special syntax for HTTP sampler regarding first argument
start {
    plan {
        group {
            // long version for HTTP request
            http(protocol: 'http', domain: 'localhost', port: 8080, path: '/app/login', method: 'GET')
            
            // short version for HTTP request
            http 'GET http://localhost:8080/app/login'
        }

        // if used with defaults keyword, some elements can be ommitted
        group {
            defaults(protocol: 'http', domain: 'localhost', port: 8080)

            http 'GET /app/login'
        }
    }
}
  • simplified array like structures for samplers and config elements
// test_1.groovy
start {
    // long version to define user variables inside test plan
    plan {
        arguments {
            argument(name: 'var_variable', value: 'value')
            argument(name: 'var_other_variable', value: 'other_value')
        }
    }
}

// test_2.groovy
start {
    // short version to define user variables inside test plan
    plan {
        arguments values: [
                var_variable      : 'value',
                var_other_veriable: 'other_value'
        ]
    }
}

// test_3.groovy
start {
    // there are others test elements which has simplified behaviour e.g. params, variables, headers
    plan {
        group {
            // long version for params
            http 'GET http://www.example.com', {
                params {
                    param(name: 'param', value: 'value')
                    param(name: 'other', value: 'other')
                }
            }
            // short version for params
            http 'GET http://www.example.com', {
                params values: [
                        param: 'value',
                        other: 'other'
                ]
            }
        }
    }
}
  • by default samples have names after its name property, but in case of HTTP request there are some differences:
start {
    plan {
        arguments values: [ var_bookId: 'value' ]

        group {
            // the name of the sample will generated as 'GET /app/books/:var_bookId'
            // currently this is default behaviour only if short version is used
            http 'GET http://localhost/app/books/${var_bookId}'
            // to define own sample name you must use long version
            http name: 'Custom Name', protocol: 'http', domain: 'localhost', path: '/app/books/${var_bookId}', method: 'GET'
        }
    }
}

groovy-jmeter's People

Contributors

antoniosun avatar smicyk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

antoniosun

groovy-jmeter's Issues

Running the examples/application

Hi @smicyk,

What would I be expecting when running the examples/application?

I would be expecting some jmeter (or any test script) execution logs showing up, but I've been waiting for over 10 minutes, but didn't see anything, except that, after it is up for a while, I got:

testserver-app  | WARNING: An illegal reflective access operation has occurred
testserver-app  | WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v9.Java9 (file:/opt/groovy/lib/groovy-3.0.7.jar) to method sun.nio.fs.UnixFileSystem.getPath(java.lang.String,java.lang.String[])
testserver-app  | WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.vmplugin.v9.Java9
testserver-app  | WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
testserver-app  | WARNING: All illegal access operations will be denied in a future release
testserver-app  | Caught: java.nio.file.AccessDeniedException: jmeter.properties
testserver-app  | java.nio.file.AccessDeniedException: jmeter.properties
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
testserver-app  |       at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase$_updateJMeterPropertiesLocation_closure7.doCall(TestScriptBase.groovy:123)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
testserver-app  |       at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase.updateJMeterPropertiesLocation(TestScriptBase.groovy:122)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
testserver-app  |       at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase.start(TestScriptBase.groovy:44)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase$start$0.callCurrent(Unknown Source)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase.start(TestScriptBase.groovy:40)
testserver-app  |       at net.simonix.dsl.jmeter.TestScriptBase$start.callCurrent(Unknown Source)
testserver-app  |       at script.executeScript(script.groovy:6)
testserver-app  |       at net.simonix.dsl.jmeter.TestScript.run(TestScript.groovy:133)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
testserver-app  |       at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
testserver-app  |       at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
testserver-app exited with code 1

Will there be test script execution logs normally? Or the problem is caused entirely by the testserver-app exited? thx

[Feature] Allow loops: -1 for infinite loop

I like to spell out all the group requied parameters, like this:

    group(name: 'Thread Group', delay: c_lt_delay, delayedStart: true,
      users: c_lt_users, rampUp: c_lt_ramp, keepUser: false,
      loops: c_lt_loops, duration: c_lt_duration, scheduler: true) {

However, most often I need to flip between infinite loop and timed test back and force. Allowing loops: -1 as indication of infinite loop will make the flipping much easer than changing the above code, as I would like to be able to change the behaviour of the load by just tweaking those parameters.

Currently it'll just error out:

net.simonix.dsl.jmeter.model.ValidationException: The property 'loops' has invalid value. The value should be from range 1..9223372036854775807?

Whereas if you put -1 in jmeter as the loop value, jmeter will make it an infinite loop right afterwards.

PS. Maybe the algorithm should be <=0, the reason I like negative valuses instead of 0 more is because it'll be much siimple to add and remove the - sign, than changing the value to 0, and having difficulties to remember what value it was when changing it back.

[Q] POST Text instead of Params

Following up on #71, here is another POST outlier.

First of all, here is a sample of normal POST in a .har file:

image

The POST has Params in PostData.

Now,

image

This one, The POST has Text in PostData instead.

How to send such Text in POST?

Would that be something like this?

            http('POST /url/...') {
                body "POST Text here"
            }

code caching issue

Hi @smicyk

Please take a look at these two lines,

https://github.com/AntonioSun/gjs-xrpl/blob/10219373d6660ee71e466ff2b58d7839bb9bbd32/book_offers/book_offers_ctrl.groovy#L45-L46

I found that flipping the two lines will get me exactly same output, even though the underlying code are a bit different (the InfluxDb Backend being enabled or not). There are more caching issues, if you checkout the code history step by step, but this one is the easiest to duplicate, for me.

Are you getting the same result as well? thx

[feature] test fragment for inclusion

Hi, Interesting project that I just noticed today. Quick question,

Does groovy-jmeter support feature similiar to jmeter fragment so that people can define common scripts to be included in other scripts?

thx

ExceptionInInitializerError, SecurityException: Can not initialize cryptographic mechanism

Hi sorry to bother you @smicyk,

I'm facing the SecurityException error, with the example from readme, that I have no idea how to fix:

$ cat example.groovy
@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script

start {
    plan {
        group {
            http 'GET http://www.example.com'
        }

        // optional element, shows execution progress
        summary(file: 'log.jtl')
    }
}

$ groovy example.groovy --help
Caught: java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
        at org.apache.ivy.util.url.BasicURLHandler.getURLInfo(BasicURLHandler.java:95)
        at org.apache.ivy.plugins.repository.url.URLResource.init(URLResource.java:82)
        at org.apache.ivy.plugins.repository.url.URLResource.exists(URLResource.java:101)
        at org.apache.ivy.plugins.resolver.IBiblioResolver.listRevisionsWithMavenMetadata(IBiblioResolver.java:494)
        at org.apache.ivy.plugins.resolver.IBiblioResolver.listRevisionsWithMavenMetadata(IBiblioResolver.java:485)
        at org.apache.ivy.plugins.resolver.IBiblioResolver.listResources(IBiblioResolver.java:438)
        at org.apache.ivy.plugins.resolver.RepositoryResolver.findDynamicResourceUsingPattern(RepositoryResolver.java:150)
        at org.apache.ivy.plugins.resolver.RepositoryResolver.findResourceUsingPattern(RepositoryResolver.java:135)
        at org.apache.ivy.plugins.resolver.AbstractPatternsBasedResolver.findResourceUsingPatterns(AbstractPatternsBasedResolver.java:94)
        at org.apache.ivy.plugins.resolver.IBiblioResolver.findIvyFileRef(IBiblioResolver.java:109)
        at org.apache.ivy.plugins.resolver.BasicResolver.getDependency(BasicResolver.java:233)
        at org.apache.ivy.plugins.resolver.IBiblioResolver.getDependency(IBiblioResolver.java:557)
        at org.apache.ivy.plugins.resolver.ChainResolver.getDependency(ChainResolver.java:101)
        at org.apache.ivy.core.resolve.IvyNode.loadData(IvyNode.java:185)
        at org.apache.ivy.core.resolve.VisitNode.loadData(VisitNode.java:284)
        at org.apache.ivy.core.resolve.ResolveEngine.fetchDependencies(ResolveEngine.java:719)
        at org.apache.ivy.core.resolve.ResolveEngine.doFetchDependencies(ResolveEngine.java:798)
        at org.apache.ivy.core.resolve.ResolveEngine.fetchDependencies(ResolveEngine.java:726)
        at org.apache.ivy.core.resolve.ResolveEngine.getDependencies(ResolveEngine.java:604)
        at org.apache.ivy.core.resolve.ResolveEngine.resolve(ResolveEngine.java:250)
        at org.apache.ivy.Ivy.resolve(Ivy.java:522)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Caused by: java.lang.SecurityException: Can not initialize cryptographic mechanism
        ... 24 more
Caused by: java.lang.SecurityException: Missing mandatory jurisdiction policy files: unlimited
        ... 24 more

$ java -version
java version "17.0.3" 2022-04-19 LTS
Java(TM) SE Runtime Environment (build 17.0.3+8-LTS-111)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.3+8-LTS-111, mixed mode, sharing)

$ groovy -version
Groovy Version: 3.0.7 JVM: 17.0.3 Vendor: Oracle Corporation OS: Linux

$ gradle -version

------------------------------------------------------------
Gradle 7.0.2
------------------------------------------------------------

Build time:   2021-05-14 12:02:31 UTC
Revision:     1ef1b260d39daacbf9357f9d8594a8a743e2152e

Kotlin:       1.4.31
Groovy:       3.0.7
Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM:          17.0.3 (Oracle Corporation 17.0.3+8-LTS-111)
OS:           Linux 4.4.0-22000-Microsoft amd64

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 11 (bullseye)
Release:        11
Codename:       bullseye

"OS: Linux 4.4.0-22000-Microsoft". I.e., it is Debian within WSL.

Thanks

Troubleshoot for no output

Running under Mac:

$ java -version
openjdk version "11.0.21" 2023-10-17
OpenJDK Runtime Environment Homebrew (build 11.0.21+0)
OpenJDK 64-Bit Server VM Homebrew (build 11.0.21+0, mixed mode)

$ groovy -v
Groovy Version: 3.0.17 JVM: 11.0.21 Vendor: Homebrew OS: Mac OS X

$ groovy example0.groovy --help
Usage: groovy [-chw] [--no-run] [--jmx-out=<file>] [-r=<hostname:port>]
. . .

$ groovy example0.groovy
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v9.Java9 (file:/Users/myid/.sdkman/candidates/groovy/current/lib/groovy-3.0.17.jar) to method sun.nio.fs.UnixFileSystem.getPath(java.lang.String,java.lang.String[])
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.vmplugin.v9.Java9
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
Warning: Nashorn engine is planned to be removed from a future JDK release

$ cat example0.groovy
@GrabConfig(systemClassLoader=true)
@Grab('net.simonix.scripts:groovy-jmeter')

@groovy.transform.BaseScript net.simonix.dsl.jmeter.DefaultTestScript script

start {
    plan {
        group {
            http 'GET http://www.example.com'
        }

        // optional element, shows execution progress
        summary(file: 'log.jtl')
    }
}

The typical output on console is not there.
How to troubleshoot the problem pls?

Update prerequisites version please

Hi @smicyk How are you. Happy 2024!

I'm setting up my groovy-jmeter again, is it possible to bring the prerequisites versions to the following pls?

  • groovy @4.0.17
  • gradle @8.5

These are the versions my MacPorts reports currently, which can save me lots of struggle going back to old versions.

UPDATE:

Hmm... I cannot remember why the versions have to be match, but I do remember there has been a time when things were not working when the versions were not matched. Ah, found it #52 (comment).

Thanks

Another InfluxDb BackendListener

Hi @smicyk,

I'm switching to another InfluxDb Backend Listener, which is of class rocks.nt.apm.jmeter.JMeterInfluxDBBackendListenerClient

I first try follow the https://github.com/smicyk/groovy-jmeter/blob/develop/examples/plugins/datadog/script.groovy
but got:

WARNING: Could not find match for name 'rocks.nt.apm.jmeter.JMeterInfluxDBBackendListenerClient'
Caught: net.simonix.dsl.jmeter.model.ValidationException: The keyword 'rocks.nt.apm.jmeter.JMeterInfluxDBBackendListenerClient' is not valid. Did you misspell any of valid keywords [execute_if, argument, before, jsrpreprocessor, csv, simple, section, execute_total, uniform_timer, jsrsampler, jsrassertion, authorization, execute_random, assert_duration, view, assert_jmes, loop, host, throughput, jsrlistener, jdbc_request, plan, flow, group, jsrpostprocessor, extract_json, cache, cookie, check_request, dns, execute_percent, execute, poisson_timer, execute_order, java_request, execute_interleave, execute_once, execute_runtime, defaults, http, header, extract_regex, check_size, assert_md5hex, authorizations, for_each, insert, constant_timer, jdbc, gaussian_timer, login, check_response, cookies, aggregate, timer, random, extract_xpath, precise_throughput, jsrtimer, backend, after, graphql, constant_throughput, assert_json, jdbc_config, summary, include, headers, variables, debug, ajp, assert_response, assert_xpath, extract_jmes, counter, extract_css, jdbc_preprocessor, schedule, execute_while, synchronizing_timer, jdbc_postprocessor, execute_switch, variable, assert_size, debug_postprocessor, arguments, transaction]?

I then try follow the
https://github.com/smicyk/groovy-jmeter/blob/develop/examples/standalone/script.groovy

Generating .jmx succeeded, but I got this:
image

Would you support this new InfluxDb Backend Listener please?

The desired xml output is:

      <BackendListener guiclass="BackendListenerGui" testclass="BackendListener" testname="Backend Listener" enabled="true">
        <elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
          <collectionProp name="Arguments.arguments">
            <elementProp name="testName" elementType="Argument">
              <stringProp name="Argument.name">testName</stringProp>
              <stringProp name="Argument.value">${c_cfg_TestName}</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="nodeName" elementType="Argument">
              <stringProp name="Argument.name">nodeName</stringProp>
              <stringProp name="Argument.value">${TestDesc}</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="runId" elementType="Argument">
              <stringProp name="Argument.name">runId</stringProp>
              <stringProp name="Argument.value">${runId}</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="influxDBHost" elementType="Argument">
              <stringProp name="Argument.name">influxDBHost</stringProp>
              <stringProp name="Argument.value">xyz</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="influxDBPort" elementType="Argument">
              <stringProp name="Argument.name">influxDBPort</stringProp>
              <stringProp name="Argument.value">8086</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="influxDBUser" elementType="Argument">
              <stringProp name="Argument.name">influxDBUser</stringProp>
              <stringProp name="Argument.value">jmeter</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="influxDBPassword" elementType="Argument">
              <stringProp name="Argument.name">influxDBPassword</stringProp>
              <stringProp name="Argument.value">PW</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="influxDBDatabase" elementType="Argument">
              <stringProp name="Argument.name">influxDBDatabase</stringProp>
              <stringProp name="Argument.value">jmeter</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="retentionPolicy" elementType="Argument">
              <stringProp name="Argument.name">retentionPolicy</stringProp>
              <stringProp name="Argument.value">autogen</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="samplersList" elementType="Argument">
              <stringProp name="Argument.name">samplersList</stringProp>
              <stringProp name="Argument.value">abc.*</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="useRegexForSamplerList" elementType="Argument">
              <stringProp name="Argument.name">useRegexForSamplerList</stringProp>
              <stringProp name="Argument.value">true</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="recordSubSamples" elementType="Argument">
              <stringProp name="Argument.name">recordSubSamples</stringProp>
              <stringProp name="Argument.name">recordSubSamples</stringProp>
              <stringProp name="Argument.value">true</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
          </collectionProp>
        </elementProp>
        <stringProp name="classname">rocks.nt.apm.jmeter.JMeterInfluxDBBackendListenerClient</stringProp>
      </BackendListener>

Changes need from the org.apache.jmeter.visualizers.backend.influxdb.InfluxdbBackendListenerClient is posted at
https://www.diffchecker.com/NZbFVuXT

Thanks

Load control

group(users: users, rampUp: duration) {

Questions

  • How are the users duration variables passed? I see the calling script to be
    groovy script.groovy --jmx-out script.jmx -Vvar1=value1 -Vvar2=value2 -Vusers=1 -Vduration=10
    but I haven't find where the users duration are specified.

  • To control the load, JMeter also has/need a third parameter --how long the script should run, or how many iterations. When such info is missing, how the user-load is exactly happening when groovy-jmeter scripts are being run?

Thanks!

Debug Sample

From

      debug displayJMeterVariables: true, displayJMeterProperties: true

I'm getting:

image

I.e., the displayJMeterProperties: true has no effect.

Common default cookie manager

How to set a default cookie manager to all my requests?

    group(users: jmt_users, rampUp: jmt_ramp) {
      cookies()

This cookies() applies to all my requests under this group?

Failed to create component for 'insert'

I got Failed to create component for 'insert'. Here is the way to replicate the problem:

  • Copy the script.groovy from structure to structure-2 folder as-is.
  • structure-2 is parallel to structure folder
  • script.groovy works in the structure folder just fine, but
  • For script.groovy under structure-2 to refer to structure, I made a simple change, to replace all insert 'fragments/xxx' as insert '../structure/fragments/xxx', but groovy-jmeter just cannot handle it any more:
Caught: java.lang.RuntimeException: Failed to create component for 'insert' reason: java.lang.NullPointerException: Cannot invoke method toURI() on null object
java.lang.RuntimeException: Failed to create component for 'insert' reason: java.lang.NullPointerException: Cannot invoke method toURI() on null object
        at script$_executeScript_closure1$_closure2.doCall(script.groovy:14)
        at script$_executeScript_closure1$_closure2.doCall(script.groovy)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at script$_executeScript_closure1.doCall(script.groovy:7)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at net.simonix.dsl.jmeter.TestScriptRunner.invokeBuilder(TestScriptRunner.groovy:175)
        at net.simonix.dsl.jmeter.TestScriptRunner$invokeBuilder$0.callStatic(Unknown Source)
        at net.simonix.dsl.jmeter.TestScriptRunner.configure(TestScriptRunner.groovy:154)
        at net.simonix.dsl.jmeter.TestScriptRunner$configure.call(Unknown Source)
        at net.simonix.dsl.jmeter.TestScriptBase.start(TestScriptBase.groovy:96)
        at net.simonix.dsl.jmeter.TestScriptBase$start$0.callCurrent(Unknown Source)
        at net.simonix.dsl.jmeter.TestScriptBase.start(TestScriptBase.groovy:42)
        at net.simonix.dsl.jmeter.TestScriptBase$start.callCurrent(Unknown Source)
        at script.executeScript(script.groovy:6)
        at net.simonix.dsl.jmeter.TestScript.run(TestScript.groovy:150)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
Caused by: java.lang.NullPointerException: Cannot invoke method toURI() on null object
        at net.simonix.dsl.jmeter.factory.AbstractTestElementFragmentFactory.newInstance(AbstractTestElementFragmentFactory.groovy:58)
        ... 22 more

Make use of GitHub Discussion?

Hi @smicyk,

Thank you for all your answers.
Sorry to have spammed your issue with so many questions, and I actually have many more.
So it makes me think, may be it is time to enable GitHub Discussions in this repo?

Please take a look at

Basically,

To Enabling GitHub Discussions

  1. Open your repository settings and go to “Features”
  2. Check the box next to “Discussions”
  3. Create a welcome post to start your forum.

View Results Tree defaults

Following up on #59

As per the guideline of

All keywords in the DSL has predefined default values for its properties. For example, each keyword has name property with a default value defined. If there is no name property given for the keyword, the script will use the default value.

I'm proposing the view() to have the following default values:

  • name: 'View Result Tree' or even blank if possible,
  • file: not required as this is view only by default
  • enabled: true`

These are the defaults when you add a View Results Tree component to JMeter GUI.

I.e., this need to be fixe:

Caught: net.simonix.dsl.jmeter.model.ValidationException: The keyword 'view' is missing required properties. Did you forget to add any of the required properties [file]?

Comments?

Failures running myscript

docker run --rm -u groovy -v "$(pwd)":"/home/groovy" -v "grapes-cache":"/home/groovy/.groovy/grapes" --network network-books groovy:3.0.7-jdk11 groovy script.groovy -Vhost_ip=mockserver-books -Vinflux_ip=influxdb-books

Both the above and README suggest running my scripts should be as simple as that. However this is what I get when tried:

$ docker run --rm -u groovy -v "$(pwd)":"/home/groovy" -v "grapes-cache":"/home/groovy/.groovy/grapes" groovy:3.0.7-jdk11 groovy myscript.groovy 
Caught: java.io.FileNotFoundException: /tmp/hsperfdata_groovy/myscript.groovy (/tmp/hsperfdata_groovy/myscript.groovy)
java.io.FileNotFoundException: /tmp/hsperfdata_groovy/myscript.groovy (/tmp/hsperfdata_groovy/myscript.groovy)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

Something has change since the doc and README were written?

groovy expression in jmeter file

Ah ok, so you should look at https://github.com/smicyk/groovy-jmeter#groovy-as-dsl.

So basically you have two types of variables substitutions, the one for groovy variables and one for jmeter variables. Since jmeter and has same syntax to substitute it can be confusing. In general the rule is if you want groovy substitution you should use double quotes, in any other case you should use single quotes.

In your case when you have:

argument(name: 'var_host', value: "${jmt_host}")

you define var_host variable based on groovy variable jmt_host and this substitution is only available during test plan build phase.

then

defaults(protocol: 'http', domain: '${var_host}', port: 1080) 

you use var_host variable define before during test plan execution.

Note, that groovy substitution you don't have use string interplation to get variable and just use the variable name. It depends what you want to actually create as a variable.

Originally posted by @smicyk in #53 (comment)

Summary Report: Include group name in the label?

In the Summary Report, there is an "Include group name in the label?" option -

image

Is such option controllable with groovy-jmeter? thx

BTW: If “Include group name in the label?” is selected, then the name of the thread group is added as a prefix. This allows identical labels from different thread groups to be collated separately if required.

View Results Tree support?

First of all, thanks a lot for providing the jmespath support in such a swift way. Now,

I know it is debatable whether View Results Tree is necessary, but whenever something unexpected happens, or the groovy-jmeter
or the plain jmeter script is not behaving as I expected, the first thing I'd do is to add the View Results Tree, so that I can know what went wrong.

That might be the only way to debug groovy-jmeter issue. Please consider. thx.

[feature] Jmeter Timers

From https://www.guru99.com/timers-jmeter.html

By default, JMeter sends the request without pausing between each request. In that case, JMeter could overwhelm your test server by making too many requests in a short amount of times.
in real life visitors do not arrive at a website all at the same time, but at different time intervals. So Timer will help mimic the real-time behavior.

Please add them, at least the Uniform random timer one, which would be most useful. thx.

100.00% Err when running scripts with parameters

plan {
arguments {
argument(name: 'var_host', value: 'localhost')
argument(name: 'var_user_data', value: "${var1}")
argument(name: 'var_user_data1', value: "${var2}")
}

Based on that, I'm running my script of
https://gist.githubusercontent.com/AntonioSun/d3068084f03fb5e5f2a63b9f3e73c5e1/raw/9e3a7e6922047aedf0520e22f74156e26f28519d/commandline.groovy

and this is what I got:

$ docker run --rm -u groovy -v "$(pwd)":"/home/groovy" -v "grapes-cache":"/home/groovy/.groovy/grapes" groovy:3.0.7-jdk11 groovy script.groovy -Vjmt_host=172.24.24.1 -Vjmt_users=3 -Vjmt_ramp=1 -Vjmt_user_nm=john -Vjmt_user_pw=john 
WARNING: An illegal reflective ...
 =     42 in 00:00:01 =   39.7/s Avg:     0 Min:     0 Max:     0 Err:    42 (100.00%)

This is not the first time that I'm seeing 100.00% Err when running groovy-jmeter scripts. In fact all of them are, as of now.

I posted the execution log here. This is actually my 3rd run, previously every line has problems, now at least the top few lines are OK.

The converted jmeter script runs fine in jmeter. I posted the jmeter script here --
https://gist.githubusercontent.com/AntonioSun/d3068084f03fb5e5f2a63b9f3e73c5e1/raw/77f4d18a970c6d8c2bdc57177649cb1bd8290094/commandline.jmx

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.