Dekompozice JMeter testů: JMX soubor o 50 000 řádcích zredukován na 5 000
Monolitické JMX soubory JMeter s více než 50 000 řádky komplikují code review, úpravy a údržbu. Soubor se nevejde do paměti, merge konflikty při paralelní práci jsou nevyhnutelné a diff v PR o 3000 řádcích XML je nečitelný. JSR223 Groovy a SQL skripty uvnitř JMX postrádají zvýraznění syntaxe v IDE.
Dekompozice zredukovala JMX na 5000 řádků, logika testů byla přesunuta do samostatných souborů. Testování endpointu se zkrátilo z 900 na 300 řádků. Struktura projektu se stala modulární, jako v běžném kódu.
Přesun SQL skriptů do samostatných souborů
JDBC Request ukládá SQL uvnitř JMX bez zvýraznění syntaxe. Funkce __FileToString čte .sql soubory, ale nerozvíjí JMeter proměnné ${varName} z vars.
Řešením je univerzální JSR223 Sampler v Groovy. Skript dosadí proměnné, provede SELECT/DML a uloží výsledky do vars:
sql_result_count— počet řádkůsql_col_<N>— názvy sloupcůsql_<řádek>_<sloupec>— hodnotysql_result— první hodnotasql_affected_rows— změněné řádky pro DML
import groovy.sql.Sql
import java.util.regex.Pattern
// ============================================================
// JMeter JSR223 Sampler - Univerzální SQL executor
// ============================================================
// Parametry (oddělené mezerou):
// args[0] = JDBC URL (jdbc:postgresql://host:5432/db)
// args[1] = uživatelské jméno DB
// args[2] = heslo DB
// args[3] = cesta k .sql souboru (lze i s mezerami)
//
// Výsledek se zapíše do JMeter vars:
// sql_result_count - počet řádků
// sql_col_count - počet sloupců
// sql_col_<N> - název sloupce N (index od 1)
// sql_<řádek>_<sloupec> - hodnota (index od 1, např. sql_1_1)
// sql_result - první hodnota (zkratka pro skalární hodnotu)
// ============================================================
def jdbcUrl = args[0]
def dbUser = args[1]
def dbPass = args[2]
def sqlFile = args[3..args.length - 1].join(" ")
// --- Čtení SQL ze souboru ---
def rawSql = new File(sqlFile).getText("UTF-8")
// --- Dosazení ${varName} -> vars.get("varName") ---
def resolved = rawSql.replaceAll(/\$\{(\w+)\}/) { fullMatch, varName ->
def value = vars.get(varName)
if (value == null) {
log.warn("Proměnná '${varName}' nebyla nalezena v JMeter vars, ponechává se původní")
return fullMatch
}
return value
}
log.info("Provádím SQL:\n${resolved}")
// --- Připojení a provedení ---
def sql = Sql.newInstance(jdbcUrl, dbUser, dbPass, "org.postgresql.Driver")
try {
def trimmed = resolved.stripIndent().trim()
def isSelect = trimmed.toUpperCase() =~ /^\s*(SELECT|WITH|VALUES)\b/
if (isSelect) {
// --- SELECT / WITH / VALUES - očekává se ResultSet ---
def rows = sql.rows(resolved)
if (rows == null || rows.isEmpty()) {
vars.put("sql_result_count", "0")
vars.put("sql_col_count", "0")
vars.put("sql_result", "")
log.info("SQL vrátilo 0 řádků")
return
}
def colNames = rows[0].keySet().toList()
vars.put("sql_result_count", String.valueOf(rows.size()))
vars.put("sql_col_count", String.valueOf(colNames.size()))
colNames.eachWithIndex { name, idx ->
vars.put("sql_col_${idx + 1}", name)
}
rows.eachWithIndex { row, rowIdx ->
colNames.eachWithIndex { col, colIdx ->
def val = row[col]
vars.put("sql_${rowIdx + 1}_${colIdx + 1}", val != null ? val.toString() : "")
}
}
def firstVal = rows[0][colNames[0]]
vars.put("sql_result", firstVal != null ? firstVal.toString() : "")
log.info("SQL vrátilo ${rows.size()} řádek(ů), ${colNames.size()} sloupc(ů)")
} else {
// --- DML (INSERT/UPDATE/DELETE/MERGE atd.) ---
def affected = sql.executeUpdate(resolved)
vars.put("sql_result_count", "0")
vars.put("sql_col_count", "0")
vars.put("sql_result", "")
vars.put("sql_affected_rows", String.valueOf(affected))
log.info("DML provedeno, ovlivněné řádky: ${affected}")
}
} catch (Exception e) {
log.error("Provedení SQL selhalo: ${e.message}", e)
throw e
} finally {
sql.close()
}
Parametry v JSR223 Sampler: JDBC URL, uživatelské jméno, heslo, cesta k .sql. Relativní cesty fungují v GUI i CLI při shodě pracovních adresářů.
Groovy skripty mimo JMX
JSR223 prvky se přesouvají do .groovy souborů. V nastavení se zapne Cache compiled script if available — bez toho kompilace při každém volání snižuje výkon.
Společné utility (sql_executor.groovy, json_utils.groovy) jsou ve složce common a jsou dostupné všem testům.
Modularita pomocí Test Fragment a Include Controller
Thread Group podle domén se uloží jako Test Fragment (PCM → Save as Test Fragment). Do hlavního JMX se přidají pomocí Include Controller.
Úskalí:
- V CLI se cesty řeší od pracovního adresáře, ne od JMX
- V CI/CD je třeba nastavit
cddo adresáře projektu
Struktura projektu po refaktoringu
Předtím:
project/
└── src/test/jmeter/
└── test_plan.jmx # 50 000+ řádků
Nyní:
project/
└── src/test/jmeter/
├── test_plan.jmx # ~5 000 řádků
├── common/
│ ├── sql_executor.groovy
│ └── json_utils.groovy
├── domain_a/
│ ├── domain_a_fragment.jmx
│ ├── create_entity.sql
│ ├── cleanup.sql
│ ├── pre_processing.groovy
│ └── response_validation.groovy
├── domain_b/
│ ├── domain_b_fragment.jmx
│ ├── prepare_data.sql
│ └── check_result.groovy
└── domain_c/
├── domain_c_fragment.jmx
├── init.sql
└── assertions.groovy
JMX obsahuje pouze sekvenci provádění. Logika je ve specializovaných souborech s podporou IDE.
Co je důležité
- Velikost JMX se zmenšila 10×: 50 000 → 5000 řádků
- SQL a Groovy mimo JMX s zvýrazněním syntaxe a automatickým doplňováním
- Test Fragment + Include Controller pro modularitu
- Univerzální SQL skript s dosazením
${varName} - Cache compiled script je povinný pro Groovy
— Editorial Team
Zatím žádné komentáře.