{"id":13650882,"url":"https://github.com/smicyk/groovy-jmeter","last_synced_at":"2025-08-22T03:32:49.227Z","repository":{"id":38019150,"uuid":"303487213","full_name":"smicyk/groovy-jmeter","owner":"smicyk","description":"A Groovy-based DSL for building and running JMeter test plans from command line and more.","archived":false,"fork":false,"pushed_at":"2024-02-14T22:33:36.000Z","size":1007,"stargazers_count":13,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2024-05-07T18:13:06.368Z","etag":null,"topics":["command-line","dsl","groovy","groovy-script","jmeter","monitoring","performance","scripts","testing"],"latest_commit_sha":null,"homepage":"","language":"Groovy","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/smicyk.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-10-12T19:03:29.000Z","updated_at":"2024-03-21T09:06:13.000Z","dependencies_parsed_at":"2024-02-13T23:22:21.933Z","dependency_job_id":"33659b22-72c2-426f-8321-bfe537691fa1","html_url":"https://github.com/smicyk/groovy-jmeter","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/smicyk/groovy-jmeter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smicyk%2Fgroovy-jmeter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smicyk%2Fgroovy-jmeter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smicyk%2Fgroovy-jmeter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smicyk%2Fgroovy-jmeter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/smicyk","download_url":"https://codeload.github.com/smicyk/groovy-jmeter/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/smicyk%2Fgroovy-jmeter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271579449,"owners_count":24784252,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-22T02:00:08.480Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line","dsl","groovy","groovy-script","jmeter","monitoring","performance","scripts","testing"],"created_at":"2024-08-02T02:00:42.000Z","updated_at":"2025-08-22T03:32:48.214Z","avatar_url":"https://github.com/smicyk.png","language":"Groovy","funding_links":[],"categories":["Automation"],"sub_categories":["DSL"],"readme":"# Groovy JMeter DSL\n\nThe *Groovy-JMeter* project is simple DSL to write JMeter test plans.\n\n![Build \u0026 Test](https://github.com/smicyk/groovy-jmeter/actions/workflows/build.yml/badge.svg)\n\nIt has the following features:\n - keep JMeter test files (*.jmx) as a code (Groovy scripts)\n - run scripts as standalone scripts, JUnit tests or Gradle tasks\n - support of basic JMeter elements like controllers, groups, extractors and assertions\n - support of HTTP, JSR223 and JDBC JMeter samplers\n - support of JMeter listeners (includes a listener with backed systems like influxdb)\n - add modularization of the script with *insert* keyword (can insert part of the other test script)\n\n Current version uses [Apache JMeter 5.6.3](https://archive.apache.org/dist/jmeter/binaries/) as runtime engine.\n\n \u003e Note, that you don't have to download any components of JMeter to run the scripts, all necessary components are initialized at startup.\n \n \u003e Checkout the project [wiki](https://github.com/smicyk/groovy-jmeter/wiki) for quick reference of all keywords and properties\n\n \u003e Check [Component Status](https://github.com/smicyk/groovy-jmeter/wiki/Components-Status) page for supported JMeter features\n\n## Prerequisites\n\nBefore start, you should have:\n\n- [Java 11+](https://openjdk.java.net/)\n- [Groovy 3.0.20](https://groovy.apache.org/download.html)\n- [Gradle 8.5](https://gradle.org/releases/) (needed only for building from source)\n\nOr you can use the [Docker](https://www.docker.com/) approach (check the section below):\n\n## How to start\n\n\u003e Starting from version 0.11.0, all artifacts are available through [maven repository](https://mvnrepository.com/artifact/net.simonix.scripts/groovy-jmeter). If you want the latest changes, go to [Building from source](#building-from-source).\n\n## First steps\n\nTo run your test script, it is enough to type in your favorite editor the following lines:\n\n```groovy\n@GrabConfig(systemClassLoader=true)\n@Grab('net.simonix.scripts:groovy-jmeter')\n\n@groovy.transform.BaseScript net.simonix.dsl.jmeter.DefaultTestScript script\n\nstart {\n    plan {\n        group {\n            http 'GET http://www.example.com'\n        }\n\n        // optional element, shows execution progress\n        summary(file: 'log.jtl')\n    }\n}\n\n```\n\nThis test plan will start one user and make call to *http://www.example.com*\n\n\u003e Please remember that first execution of the script can take some time as Ivy must download all dependency to local cache\n\nTo run the script, execute the following command:\n\n```shell script\ngroovy yourscriptname.groovy\n``` \nIf you want to start the same script inside a docker, execute commands below:\n\n```shell script\n# one time setup for script dependencies, it will speed up future executions\ndocker volume create --name grapes-cache\n\n# in Linux\ndocker run --rm -u groovy -v \"$(pwd)\":\"/home/groovy\" -v \"grapes-cache\":\"/home/groovy/.groovy/grapes\" groovy:3.0.20-jdk11 groovy yourscriptname.groovy\n\n# in Windows\ndocker run --rm -u groovy -v %CD%:\"/home/groovy\" -v \"grapes-cache\":\"/home/groovy/.groovy/grapes\" groovy:3.0.20-jdk11 groovy yourscriptname.groovy\n```\n\nTypical output on console should look like this:\n```shell script\n+      1 in 00:00:01 =    1,7/s Avg:   419 Min:   419 Max:   419 Err:     0 (0,00%) Active: 1 Started: 1 Finished: 0\n=      1 in 00:00:01 =    1,7/s Avg:   419 Min:   419 Max:   419 Err:     0 (0,00%)\n```\n\n## Test plan generator\n\nThere is [*.har converter](https://github.com/smicyk/groovy-harventer) to generate scripts from *.har files. It can greatly speed up scripts generation for your tests. \n\n## DefaultTestScript vs. TestScript\n\nThere 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.\n\nWhen you change the line with *@groovy.transform.BaseScript* annotation to *net.simonix.dsl.jmeter.TestScript*, so your file would look like this:\n\n```groovy\n\n@GrabConfig(systemClassLoader=true)\n@Grab('net.simonix.scripts:groovy-jmeter')\n\n@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script\n\nstart {\n    plan {\n        group {\n            http 'GET http://www.example.com'\n        }\n\n        // optional element, shows execution progress\n        summary(file: 'log.jtl')\n    }\n}\n\n```\n\nYou can execute your script with *help* parameter to see all available options:\n```shell script\ngroovy yourscriptname.groovy --help\nUsage: groovy [-h] [--no-run] [--jmx-out=\u003cfile\u003e] [-V=\u003cvariable=value\u003e\n              [=\u003cvariable=value\u003e]...]...\n  -h, --help             Show help message\n      --jmx-out=\u003cfile\u003e   Generate .jmx file based on the script\n      --no-run           Execute the script but don't run the test\n  -V, --vars=\u003cvariable=value\u003e[=\u003cvariable=value\u003e]...\n                         Define values for placeholders in the script\n```\nSome interesting usage might be to generate .jmx file with additional variables which comes from environment:\n```groovy\n@GrabConfig(systemClassLoader=true)\n@Grab('net.simonix.scripts:groovy-jmeter')\n\n@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script\n\nstart {\n    plan {\n        // HTTP defaults test element\n        defaults protocol: 'http', domain: \"${var_host}\", port: var_port\n\n        group (users: 10) {\n            http 'GET /books'\n        }\n\n        // optional element, shows execution progress\n        summary(file: 'log.jtl')\n    }\n}\n\n```\nTo generate .jmx file from this script you can execute the following in the command line:\n```shell script\ngroovy yourscriptname.groovy --jmx-out yourscriptname.jmx --no-run -Vvar_host=localhost -Vvar_port=8080\n```\n\nFor more examples you should check the [examples](examples) folder and [unit tests](src/test).\n\nTo get more information about all available options you should check groovy docs page or generate it from the source.\n```shell script\ngradlew groovydoc\n\ncd ./build/docs/groovydoc \u0026\u0026 index.html\n```\n\n## More advanced example\n\nThe example below shows more constructs related to testing simple REST API.\n\n```groovy\n@GrabConfig(systemClassLoader=true)\n@Grab('net.simonix.scripts:groovy-jmeter')\n\n@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script\n\nstart {\n    // define basic test plan\n    plan {\n        // add user define variables (this will be defined on Test Plan not as separate User Defined Variables test element)\n        arguments {\n            argument(name: 'var_host', value: 'prod.mycompany.com')\n        }\n\n        // define HTTP default values\n        defaults(protocol: 'http', domain: '${var_host}', port: 1080)\n\n        // define group of 1000 users with ramp up period 300 seconds\n        group(users: 1000, rampUp: 300) {\n            // add cookie manager for HTTP requests\n            cookies(name: 'cookies manager')\n\n            // load users data from file into variables\n            csv(file: 'users.csv', variables: ['var_username', 'var_password'], delimiter: ';')\n\n            // define POST request with parameters and extract tracking id for later use\n            http('POST /login') {\n                params {\n                    param(name: 'username', value: '${var_username}')\n                    param(name: 'password', value: '${var_password}')\n                }\n\n                extract_regex expression: '\"trackId\", content=\"([0-9]+)\"', variable: 'var_trackId'\n            }\n\n            // define GET request and extract data from json response\n            http('GET /api/books') {\n                params values: [ limit: '10' ]\n\n                extract_json expressions: '$..id', variables: 'var_bookId'\n            }\n\n            http('GET /api/books/${var_bookId}') {\n                extract_json expressions: '$..author.id', variables: 'var_authorId'\n            }\n\n            // simplified form of HTTP request, no parenthesis\n            http 'GET /api/authors/${var_authorId}'\n\n            // define simple controller to make POST request\n            simple {\n                headers values: [ 'Content-Type': 'application/json' ]\n\n                http('POST /api/books/${var_bookId}/comments') {\n                    body '''\\\n                        {\n                            \"title\": \"Title\",\n                            \"content\": \"Comment content\"\n                        }\n                    '''\n\n                    // check assertion about HTTP status code in the response\n                    check_response {\n                        status() eq 200\n                    }\n                }\n            }\n        }\n\n        // output to .jtl file\n        summary(file: 'script.jtl', enabled: true)\n    }\n}\n\n```\nThe next example shows testing database with JMeter.\n```groovy\n@GrabConfig(systemClassLoader=true)\n@Grab('net.simonix.scripts:groovy-jmeter')\n@Grab('org.postgresql:postgresql:42.2.20') // download JDBC driver for your database\n\n@groovy.transform.BaseScript net.simonix.dsl.jmeter.TestScript script\n\nstart {\n    plan {\n        before {\n            jdbc datasource: 'postgres', {\n                pool connections: 10, wait: 1000, eviction: 60000, autocommit: true, isolation: 'DEFAULT', preinit: true\n                connection url: 'jdbc:postgresql://database-db:5432/', driver: 'org.postgresql.Driver', username: 'postgres', password: 'postgres'\n                init(['SET search_path TO public'])\n                validation idle: true, timeout: 5000, query: '''SELECT 1'''\n            }\n\n            jdbc use: 'postgres', {\n                jdbc_preprocessor use: 'postgres', {\n                    execute('''\n                        DROP TABLE employee\n                    ''')\n                }\n\n                execute('''\n                    CREATE TABLE employee (id INTEGER PRIMARY KEY, first_name VARCHAR(50), last_name VARCHAR(50), email VARCHAR(50), salary INTEGER)\n                ''')\n            }\n        }\n\n        group users: 100, rampUp: 60, loops: 1, {\n            csv file: 'employees.csv', delimiter: ',', ignoreFirstLine: true, variables: ['var_id', 'var_first_name', 'var_last_name', 'var_email', 'var_salary']\n\n            jdbc use: 'postgres', {\n                execute('''\n                    INSERT INTO employee (id, first_name, last_name, email, salary) VALUES(?, ?, ?, ?, ?)\n                ''') {\n                    params {\n                        param value: '${var_id}', type: 'INTEGER'\n                        param value: '${var_first_name}', type: 'VARCHAR'\n                        param value: '${var_last_name}', type: 'VARCHAR'\n                        param value: '${var_email}', type: 'VARCHAR'\n                        param value: '${var_salary}', type: 'INTEGER'\n                    }\n                }\n            }\n\n            jdbc use: 'postgres', {\n                query(limit: 1, result: 'var_employee_count', inline: '''\n                    SELECT count(*) FROM employee\n                ''')\n\n                jsrpostprocessor(inline: '''\n                    log.info('Number of employees: ' + vars.get('var_employee_count'))\n                ''')\n            }\n\n            // output to .jtl file\n            summary(file: 'script.jtl', enabled: true)\n        }\n    }\n}\n\n```\n\n## Building from source\n\nClone, build and publish jars to your local repository:\n\n```shell script\ngit clone https://github.com/smicyk/groovy-jmeter.git\n\n# in Linux\n./gradlew clean build publishIvyPublicationToIvyRepository\n\n# in Windows\ngradlew clean build publishIvyPublicationToIvyRepository\n```\n\nYou can also try alternative approach and build everything on *Docker* without installing anything on your machine:\n\n```shell script\ngit clone https://github.com/smicyk/groovy-jmeter.git\n\ndocker volume create --name grapes-cache\n\n# in Linux\ndocker 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\n\n# in Windows\ndocker 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\n```\n\n## Conventions\n\nThere are several conventions used in the design of the DSL.\n\n### Naming\n\nAll 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__.\nHowever, 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.\n\n### Users vs. Threads\n\nIn 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:\n\n```groovy\nstart {\n    plan {\n        group users: 10, {\n            // define random variable 'var_random' for each user (in other words each user has its own random generator)\n            random(minimum: 0, maximum: 100, variable: 'var_random', perUser: true)\n        }\n    }\n}\n\n```\n\n### Default values\n\nAll 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:\n\n```groovy\nstart {\n    // would be same as plan (name: 'Test Plan')\n    plan { \n\n    }\n\n    plan {\n        // would be same as group (users: 1, rampUp: 1) \n        group {\n\n        }\n    }\n}\n\n```\n\n### Required properties\n\nEven 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:\n\n\u003e 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.\n\n```groovy\nstart {\n    plan {\n        group {\n            // condition property is required, otherwise using execute_if controller has no sense\n            execute_if (condition: '''__jexl3(vars.get('var_random') mod 2 == 0)''') {\n\n            }\n        }\n    }\n}\n\n```\n\n### Groovy as DSL\n\nThere are several things to keep in mind while writing the scripts; most of the stuff relates to Groovy language:\n\n* using different quotes around string, please refer to [Groovy docs](https://groovy-lang.org/syntax.html#all-strings}) about string and quotes and check example below:\n\n```groovy\n// test_1.groovy\nstart {\n    // using single quotes (for Java plain String)\n    plan(name: 'Test name')\n}\n\n// test_2.groovy\nstart {\n    // using single quotes is recommended in most situation (should be used when you want use JMeter variable substitution in the script)\n    plan(name: '${var_variable}')\n}\n\n// test_3.groovy\nstart {\n    // using double quotes (for GString, interpolation available during test build but not execution by JMeter engine)\n    plan (name: \"${var_param}\")\n}\n\n```\n* there are several ways to use keywords, check the example below:\n\n```groovy\n// test_1.groovy\nstart {\n    // keyword without properties, after keyword you can open closure without any properties or parenthesis\n    plan {\n\n    }\n}\n\n// test_2.groovy\nstart {\n    // keyword with properties, the properties of the keyword can have properties defined as key/value pair \n    plan(name: 'test', comments: 'new test plan') {\n\n    }\n}\n\n// test_3.groovy\nstart {\n    // keyword with properties but without parenthesis, please note that after all properties you must put comma\n    plan name: 'test', comments: 'new test plan', {\n\n    }\n}\n\n// test_4.groovy\nstart {\n    // keyword without properties and child test elements, note that the parenthesis must be used\n    plan()\n}\n\n```\n### Shortcuts\n\nThere are many places where we can use shortcuts to define the same thing:\n\n* the first argument for the keyword can be simple value. In most cases it is treated as a name for test element:\n```groovy\n// test_1.groovy\nstart {\n    // long version\n    plan name: 'Test plan111'\n}\n\n// test_2.groovy\nstart {\n    // short version\n    plan 'Test plan222'\n}\n\n// test_3.groovy\nstart {\n    // short version with properties\n    plan 'Test plan', enabled: true\n}\n\n```\n* some keywords treat first value as shortcut\n```groovy\nstart {\n    plan {\n        group {\n            // long version\n            loop count: 10\n            // short version\n            loop 10\n\n            // long version\n            execute_if condition: '''__jexl3(vars.get('var_random') mod 2 == 0)'''\n            // short version\n            execute_if '''__jexl3(vars.get('var_random') mod 2 == 0)'''\n        }\n    }\n}\n\n```\n* there is special syntax for HTTP sampler regarding first argument\n```groovy\nstart {\n    plan {\n        group {\n            // long version for HTTP request\n            http(protocol: 'http', domain: 'localhost', port: 8080, path: '/app/login', method: 'GET')\n            \n            // short version for HTTP request\n            http 'GET http://localhost:8080/app/login'\n        }\n\n        // if used with defaults keyword, some elements can be ommitted\n        group {\n            defaults(protocol: 'http', domain: 'localhost', port: 8080)\n\n            http 'GET /app/login'\n        }\n    }\n}\n\n```\n* simplified array like structures for samplers and config elements\n```groovy\n// test_1.groovy\nstart {\n    // long version to define user variables inside test plan\n    plan {\n        arguments {\n            argument(name: 'var_variable', value: 'value')\n            argument(name: 'var_other_variable', value: 'other_value')\n        }\n    }\n}\n\n// test_2.groovy\nstart {\n    // short version to define user variables inside test plan\n    plan {\n        arguments values: [\n                var_variable      : 'value',\n                var_other_veriable: 'other_value'\n        ]\n    }\n}\n\n// test_3.groovy\nstart {\n    // there are others test elements which has simplified behaviour e.g. params, variables, headers\n    plan {\n        group {\n            // long version for params\n            http 'GET http://www.example.com', {\n                params {\n                    param(name: 'param', value: 'value')\n                    param(name: 'other', value: 'other')\n                }\n            }\n            // short version for params\n            http 'GET http://www.example.com', {\n                params values: [\n                        param: 'value',\n                        other: 'other'\n                ]\n            }\n        }\n    }\n}\n\n```\n* by default samples have names after its name property, but in case of HTTP request there are some differences: \n```groovy\nstart {\n    plan {\n        arguments values: [ var_bookId: 'value' ]\n\n        group {\n            // the name of the sample will generated as 'GET /app/books/:var_bookId'\n            // currently this is default behaviour only if short version is used\n            http 'GET http://localhost/app/books/${var_bookId}'\n            // to define own sample name you must use long version\n            http name: 'Custom Name', protocol: 'http', domain: 'localhost', path: '/app/books/${var_bookId}', method: 'GET'\n        }\n    }\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmicyk%2Fgroovy-jmeter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmicyk%2Fgroovy-jmeter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmicyk%2Fgroovy-jmeter/lists"}