2

I have a CSv Data Set Config which I am using in a while loop. I have this in a module and use this same module several times in my test.

My first problem is that I have set the while loop with this condition:

${__javaScript("${data}"!="<EOF>")}

The "data" being the first column in my csv file. This works fine except for the last iteration where ${data} gets set to "EOF" and has all the tests performed on it. I want it to stop before this, not after all the tests have ran once on it.

My other problem is that when I use this module again later, ${data} is still set to "EOF" and no tests are run at all.

user3871995
  • 983
  • 1
  • 10
  • 19

3 Answers3

5

To avoid this <EOF> bit just put your logic under the If Controller and use "${data}" != "<EOF>" as the "Condition"

While Controller + IF

See Using the While Controller in JMeter guide for detailed explanation of this and other common use cases.


UPD. Reusing the same CSV file:

Add a Beanshell Sampler after 1st While Controller and before the 2nd While Controller and use the following code in "Script" area:

import org.apache.jmeter.services.FileServer;
FileServer.getFileServer().closeFile("yourfile.csv");
vars.put("data", "");

The above script will "close" the original CSV file so it could be reused later on in the script and "clear" ${data} variable as it contains <EOF> value.

See How to Use BeanShell: JMeter's Favorite Built-in Component guide for details on using Beanshell scripts in JMeter tests.

Dmitri T
  • 159,985
  • 5
  • 83
  • 133
  • Is there a way for me to set the csv file back to start, so I can use it again in a different while loop later? – user3871995 Aug 30 '16 at 12:55
  • 1
    Check update to the original answer below the horizontal line. Let me know if you need an example test plan or whatever – Dmitri T Aug 30 '16 at 13:36
  • this works fine. just add import org.apache.jmeter.services.FileServer; FileServer.getFileServer().closeFile("${testPlanFileDir}${__BeanShell(File.separator,)}testCsvFile.csv"); vars.put("varName", "1"); after the while loop. – kishor sharma Nov 05 '20 at 04:53
1

If you would use Loop Controller, with number of CSV lines being number of iterations you could avoid that. Just put this code into Beanshell Sampler:

import org.apache.commons.io.FileUtils;

int lines = FileUtils.readLines(new File("/home/username/csv.file")).size();
vars.put("linesCount", String.valueOf(lines));

After that you can use lineCount in the Loop Controller.

If your data variable needs to be reverted to original state, you could store default value in other variable, and at the end of Loop revert data to it using Beanshell pre/post proccesor.

EDIT:

Or you could insert If Controller in your While Controller and process all child elements only if data doesn't equals EOF:

${__javaScript("${data}"!="<EOF>")}
Iske
  • 1,150
  • 9
  • 18
  • I think the second option would be easier, however that won't solve my other issue for re-using the while loop and csv file again later in the test – user3871995 Aug 19 '16 at 11:19
  • Why? Is csv changing in the test? – Iske Aug 19 '16 at 11:43
  • The csv is the same file, however the while loop is in a module which gets called again later in the test. When it is called the second time, the ${data} is still at EOF – user3871995 Aug 19 '16 at 11:58
  • You could put another If Controller after the first one, which will check if data equals EOF and if so set it to what ever you want...Or you could use the first option from my answer and you would never have data being EOF. – Iske Aug 19 '16 at 12:06
0

Neither of the previous suggestions to go and re-use a csv file had worked for me. I ended up doing something different. It's way more complicated than I like, but it works.

I posted my answer to that in another post (https://stackoverflow.com/a/64086009/4832515), but I'll copy & paste it incase that link doesn't work in the future.


I couldn't find a simple solution to this. I ended up using beanshell scripts, which let you use code very similar to java to do some custom stuff. I made an example JMeter project to demonstrate how to do this (yes it's ridiculously complicated, considering all I want to do is repeat the CSV read):


  1. Files:

my file structure:

JMeterExample
|
⊢--JMeterTests.jmx             // the JMeter file
⊢--example.csv                 // the CSV file

contents of my CSV:

guest-id-1,"123 fake street",
guest-id-2,"456 fake street",
guest-id-3,"789 fake street",

so in this thread group, I'm going to just have 1 user, and I'll loop 2 times. I intend to send 1 request per CSV line. So there should be 6 requests sent total.

  1. Thread Group

enter image description here


  1. User Defined Variables

This is kind of optional, but the filepath is subject to change, and I don't like changing my scripts just for a change in configuration. So I store the CSV filename in a "User Defined Variables" node.

If you are storing the CSV file in the same directory as your JMeter test, you can just specify the filename only.

If you are saving the CSV in a folder other than the directory containing your JMeter file, you will need to supply an absolute path, and then slightly modify the beanshell script below: you'll need to comment out the line that loads the file relatively, and comment in the line that loads from an absolute path.

enter image description here


  1. BeanShell Sampler to parse and store CSV lines

Add a Beanshell Sampler which will basically take in a path, and parse & store each line as a variable. The first line will be stored as a variable called csv_line_0, the 2nd line will be csv_line_1 and so on. I know it's not a clean solution but... I can't find any clean simple way of doing this clean simple task. I copied and pasted my code below.

enter image description here

import org.apache.jmeter.services.FileServer;
import java.text.*;
import java.io.*;
import java.util.*;

String temp = null;

ArrayList lines = new ArrayList();

BufferedReader bufRdr;

ArrayList strList = new ArrayList();     

// get the file
try {
    // you can use this line below if your csvFilePath is an absolute path
    // File file = new File(${csvFilePath});

    // you can use this line below if your csvFilepath is a relative path, relative to where you saved this JMeter file
    File file = new File(org.apache.jmeter.services.FileServer.getFileServer().getBaseDir() + "/" + ${csvFilePath});

    if (!file.exists()) {
        throw new Exception ("ERROR: file " + filename + " not found");
    }

    bufRdr = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"));
} catch(Exception e){
    log.error("failed to load file");
    log.error(e.getMessage());
    return;
}

// For each CSV line, save it to a variable
int counter = 0;
while(true){
    try{
        temp = bufRdr.readLine();
        if(temp == null || temp.equals("<EOF>")){
            break;
         }
         lines.add(temp);
         vars.put("csv_line_" + String.valueOf(counter), temp);
        counter++;

        

    } catch(Exception e){
        log.error("failed to get next line");
        log.error(e.getMessage());
        break;
    }
}

// store the number of CSV lines there are for the loop counter
vars.put("linesCount", String.valueOf(lines.size()));

  1. Loop Controller

Add a Loop Controller that loops once for each CSV line. ${linesCount} is a count of the number of CSV lines and is calculated from the above beanShell script.

enter image description here


  1. Beanshell script to extract data from current CSV Line

This script will run once per CSV line. It will go and grab the current line, and parse out whatever data is on it. You'll have to modify this script to get the data you want. In my example, I only had 2 columns, where column 1 is a "guestId", and column 2 is an "address".

__jm__loopController__idx is a variable JMeter defines for you, and is the index of the loop controller. The variable name is __jm__{loop controller name}__idx.

enter image description here

String index = vars.get("__jm__loopController__idx");
String line = vars.get("csv_line_" + index);
String [] tokens = line.split(",");
vars.put("guestId", tokens[0]);
vars.put("address", tokens[1]);

  1. Http request sampler

Here's the HTTP request that's using the data extracted.

enter image description here


  1. result

When running this, as desired, I end up sending 6 http requests over to the endpoint I defined.

enter image description here

SomeGuy
  • 1,702
  • 1
  • 20
  • 19