#csv #groovy #jmeter
Вопрос:
Я использую Jmeter для отправки нескольких HTTPS — запросов в какой-либо API. Мне нужно каждый раз составлять XML-тело этих запросов с разными данными.
У меня есть csv-файл, который выглядит так:
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
.........
И у меня есть этот фрагмент кода в препроцессоре JSR223 (под образцом HTTPS), который создает несколько XML-объектов и заполняет их один за другим значениями csv и, наконец, передает этот объект Jmeter для использования в образце в качестве данных тела запроса:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
def pReqID = 'ID0000'
def numberOfNodes = 3
//input from csv file
travelDocNbr = vars.get('iDocId').toInteger();
def lines = new File("TestData\test.csv").readLines()
xml.nodes() {
1.upto(numberOfNodes, { lineNo ->
xml.object(a: 'false', b: 'false', pReqID: pReqID (lineNo).toString()) {
xml.al(ad: '2021-09-20', alc: 'bla', bla: '2021-09-20T11:00:00.000Z', sn: 'AB8912')
xml.doc(docType: lines.get(lineNo).split(',')[0],
docId: lines.get(lineNo).split(',')[1],
name: lines.get(lineNo).split(',')[2],
date: lines.get(lineNo).split(',')[3])
}
}
)
}
def nodeAsText = writer.toString()
//log.info(nodeAsText)
vars.put('passengers3XmlObj', nodeAsText)
Это всегда дает такой результат:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
</nodes>
Проблема в том, что этот скрипт начинает создавать объекты с первого значения в CSV-файле и всегда создает один и тот же XML-объект в теле каждого запроса на выборку.
Мне нужно, чтобы он каждый раз менял значения из CSV. Для этого я подумал, не могу ли я иметь переменную, которая всегда меняется на каждой итерации сэмплера, например, значение iName (я получаю другое значение на каждой итерации сэмплера благодаря элементу теста конфигурации набора данных CSV), и использовать ее, чтобы найти строку в файле CSV и начать создавать объекты с этой строки.
Так, например, учитывая, что csv-файл содержит только 3 строки, упомянутые в начале, и у меня есть значение «Dominique», сохраненное в переменной, я хочу передать его функции и получить следующий результат:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000001' name='Dominique' date='12/10/2024' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='11/15/2025' />
</object>
<object a='false' b='false' pReqID='ID00003'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='555551555' name='Braiden' date='12/31/2022' />
</object>
</nodes>
Can someone please tell me how this can be done?
UPDATE:
Thanks to @injecteer, I ended up with this code which, in my implementation, does not work as intended :
def pReqID = 'ID0000'
def numberOfNodes = 10
String startFrom = 'Dominique'
int pos = -1
// @injecteer
def text = new File("TestData\validTravellerDocuments.csv").text
// @injecteer
def csv = text.readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos amp;amp; startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] csv[ 0..<pos ] : csv
log.info reoderedCsv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
log.info "$data -> ${data.getClass()}"
}
}
def nodesAsText = writer.toString()
log.info(nodesAsText)
Текущая проблема заключается в том, что раздел заполняется таким образом:
Если я попрошу 10 узлов, я получу такое поведение:
<doc>D</doc>
...
<doc>o</doc>
...
<doc>c</doc>
...
<doc>T</doc>
...
<doc>y</doc>
...
<doc>p</doc>
...
<doc>e</doc>
...
<doc>,</doc>
etc...
Комментарии:
1. Я изо всех сил пытался понять вашу проблему и потерпел неудачу. Обеспечьте текущие и желаемые выходные данные
2. @injecteer Спасибо за отзыв. Я обновил вопрос. Надеюсь, теперь в этом больше смысла.
3. Я понимаю. Я обновил строки «плохое поведение» в вашем вопросе и пометил их
// @injecteer
4. @injecteer Большое спасибо! теперь это работает! Мне пришлось прокомментировать инструкции журнала, потому что они пытались напечатать ArrayList и log.info() применимо только для строковых типов аргументов.
Ответ №1:
Я бы изложил код естественным заводным способом.
ОБНОВЛЕНИЕ: Я добавил еще несколько строк CSV, чтобы продемонстрировать, как numberOfNodes
это влияет на вывод.
def pReqID = 'ID0000'
def numberOfNodes = 2
String startFrom = 'Dominique'
int pos = -1
def csv = """
iDocType,iDocId,iName,iDate
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
P,555551555,Braiden,2022-12-31
I,100000001,Dominique,2024-12-10
P,100000002,Joyce,2025-11-15
""".readLines().drop( 1 ).withIndex().collect{
def ( docType, docId, name, date ) = it[ 0 ].trim().split( ',' )
if( -1 == pos amp;amp; startFrom == name ) pos = it[ 1 ]
[ docType:docType, docId:docId, name:name, date:date ]
}
//here the skipped lines are reodered to the bottom, if needed
def reoderedCsv = -1 < pos ? csv[ pos..-1 ] csv[ 0..<pos ] : csv
def writer = new StringWriter()
new groovy.xml.MarkupBuilder(writer).nodes() {
reoderedCsv.take numberOfNodes eachWithIndex{ data, ix ->
object(a: 'false', b: 'false', pReqID:"$pReqID${ix 1}" ) {
al ad:'2021-09-20', alc:'bla', bla:'2021-09-20T11:00:00.000Z', sn:'AB8912'
doc data
}
}
}
println writer
С принтами:
<nodes>
<object a='false' b='false' pReqID='ID00001'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='I' docId='100000001' name='Dominique' date='2024-12-10' />
</object>
<object a='false' b='false' pReqID='ID00002'>
<al ad='2021-09-20' alc='bla' bla='2021-09-20T11:00:00.000Z' sn='AB8912' />
<doc docType='P' docId='100000002' name='Joyce' date='2025-11-15' />
</object>
</nodes>
с numberOfNodes = 3
помощью и более кода выведите желаемый вывод вашего
Комментарии:
1. Это здорово, но как я могу начать с верхней части CSV-файла, чтобы создать внутри 3 или более объектов, если я дойду до конца файла? Мне нужно определить, сколько объектов <объект><объект></объект> мне нужно внутри тегов </объект><узлы> <узлы></узлы>. Если я определяю переменную с номером 3 или 99, а длина файла невелика, мне нужно, чтобы итерация файла продолжалась от верхней части файла до нижней снова и снова и продолжала составлять объекты <объект><объект></объект> до тех пор, пока не будет достигнуто целевое число.
2. смотрите обновление..
3. хм, это может быть связано с ограничениями Дженкинса… пробовать
new File('').text.readLines()
4. ты забыл
"
в конце:log.info "$data -> ${data.getClass()}"
5. опубликуйте весь код в своем первоначальном вопросе
Ответ №2:
Если вы хотите получить количество строк, в которых Dominique
присутствует текст, это можно сделать с помощью функции findIndexOf (), например:
def dominiquePosition = lines.findIndexOf { line -> line.contains('Dominique') }
затем вы можете начать создавать свой XML-файл с позиции, полученной в приведенной выше строке:
dominiquePosition.upto(numberOfNodes, { lineNo ->
Итерация может быть получена с помощью vars.getIteration()
функции, где vars
обозначает экземпляр класса JMeterVariables. Смотрите 8 Лучших классов Java JMeter, которые вы должны использовать в статье Groovy, для получения более подробной информации об этом и других сокращениях API JMeter, доступных для тестовых элементов JSR223
Комментарии:
1. Спасибо, Дмитрий! Это работает, но в конечном итоге я получаю исключение IndexOutOfBoundsException при достижении конца CSV-файла. Как я могу смягчить это? Кроме того, вы упомянули vars.getIteration() —> Почему вы думаете, что мне это нужно ?