%% Last Updated: - [[2021-02-08]] %% The JMeter-to-k6 Converter is a tool that takes a [[JMeter]] .jmx test plan and turns it into a [[k6 (tool)]] .js script. [k6 blog post](https://k6.io/blog/announcing-jmeter-to-k6-js-converter-tool) [Repo](https://github.com/k6io/jmeter-to-k6) ## Installation ### Via NPM On [[Linux]] or [[Unix]] systems, you might run into a permissions issue when going through these instructions (I did, on [[macOS]]), so it's better to use [[Node Version Manager|NVM]] to be sure. #### Globally `npm install -g jmeter-to-k6` #### Locally, into `./node-modules` `npm install jmeter-to-k6` If you do install it locally, however, you'll also need to run it like this: `node node_modules/jmeter-to-k6/bin/jmeter-to-k6.js ...`, which is a little more lengthy. ### From DockerHub `docker pull loadimpact/jmeter-to-k6` ## Converting the file Once it's installed, all you need to do to use it is `jmeter-to-k6 -o /conversion/output/directory MyTest.jmx` where `/conversion/output/directory` is the folder where you want the .js file to be placed, and `MyTest.jmx` is the JMeter test plan you want to convert. ## Sample I created a very simple test plan in JMeter. It: - Goes to https://test.k6.io - Verifies that the response returned includes `Collection of simple web-pages suitable for load testing` - Uses a [[JMeter/Uniform Random Timer]] - Random Delay Minimum (ms): 4000 - Constant Delay Offset (ms): 1000 ### The .jmx input (JMeter) ```xml <?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <stringProp name="LoopController.loops">1</stringProp> </elementProp> <stringProp name="ThreadGroup.num_threads">1</stringProp> <stringProp name="ThreadGroup.ramp_time">1</stringProp> <boolProp name="ThreadGroup.scheduler">false</boolProp> <stringProp name="ThreadGroup.duration"></stringProp> <stringProp name="ThreadGroup.delay"></stringProp> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> </ThreadGroup> <hashTree> <TransactionController guiclass="TransactionControllerGui" testclass="TransactionController" testname="01_Home" enabled="true"> <boolProp name="TransactionController.includeTimers">false</boolProp> <boolProp name="TransactionController.parent">true</boolProp> </TransactionController> <hashTree> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.domain">test.k6.io</stringProp> <stringProp name="HTTPSampler.port"></stringProp> <stringProp name="HTTPSampler.protocol">https</stringProp> <stringProp name="HTTPSampler.contentEncoding"></stringProp> <stringProp name="HTTPSampler.path"></stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> <boolProp name="HTTPSampler.auto_redirects">false</boolProp> <boolProp name="HTTPSampler.use_keepalive">true</boolProp> <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> <stringProp name="HTTPSampler.embedded_url_re"></stringProp> <stringProp name="HTTPSampler.connect_timeout"></stringProp> <stringProp name="HTTPSampler.response_timeout"></stringProp> </HTTPSamplerProxy> <hashTree> <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> <collectionProp name="Asserion.test_strings"> <stringProp name="-745788246">Collection of simple web-pages suitable for load testing</stringProp> </collectionProp> <stringProp name="Assertion.custom_message"></stringProp> <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> <boolProp name="Assertion.assume_success">false</boolProp> <intProp name="Assertion.test_type">16</intProp> </ResponseAssertion> <hashTree/> </hashTree> </hashTree> <UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Uniform Random Timer" enabled="true"> <stringProp name="ConstantTimer.delay">1000</stringProp> <stringProp name="RandomTimer.range">4000</stringProp> </UniformRandomTimer> <hashTree/> </hashTree> </hashTree> </hashTree> </jmeterTestPlan> ``` ### The .js output (k6) ```js import http from "k6/http"; import { check, group, sleep } from "k6"; import { getRandomInt } from "./libs/compat.js"; let url, opts, r; export let options = { stages: [ { target: 1, duration: "1s", }, ], }; export default function (data) { if (__VU >= 1 && __VU <= 1) { if (__ITER < 1) { group("01_Home", () => { url = "https://test.k6.io"; opts = { redirects: 999, }; r = http.request("GET", url, "", opts); check(r, { "Response Assertion": (r) => { return r.body.includes( "Collection of simple web-pages suitable for load testing" ); }, }); }); sleep(1 + getRandomInt(0, 4000) / 1000); } } } ``` ## Limitations ## Plans for future extension ![[Announcing JMeter to K6 JS Converter Tool#^143086937]] - k6 has [[k6 Ramping Arrival Rate Executor]] - check to see if [[JMeter/Plugin/Arrivals Thread Group]] and [[JMeter/Plugin/Free-form Arrivals Thread Group]] have been implemented yet (both [[CA BlazeMeter]] plugins). ## References