Test Data Management in Performator

Every performance testing tool needs a way to load test data from a source and use it in requests, making use cases more dynamic instead of testing the same data records every time. Performator provides various data sources that help testers manage and insert test data.
In this blog, we will discuss how to set up test data management in Performator and insert it into our testing code. The examples provided have been extracted from the Performator-Quickstart project. Further documentation for starting out with Performator can be found on the Performator-Quickstart website.
The Testdata Files
In Performator, we usually keep our test data files in a Java package. This ensures they aren't stored in some random folder and that they are compiled into the .jar file when distributing our load test on agents.

Below is an excerpt from the contents of the testdata files. In this example, both the .csv-Files and the .json-Files contain the same data.
JSON:
[
{
"INDEX": 0,
"ID": "aca90d7d-e59e-4c91-b17",
"USER": "u.bjoerk",
"FIRSTNAME": "Uranus",
"LASTNAME": "Bjoerk",
"LOCATION": "Aztlan",
"EMAIL": "u.bjoerk@aztlan.com",
"LIKES_TIRAMISU": false,
"VALUE": 66,
"SEARCH_FOR": "Gianduiotto",
"TAGS": [
"rebellious",
"over-demanding",
"100% confused"
],
"OBJECT": {
"active": false,
"score": 40.4,
"status": "DONE"
}
},
...
]CSV:
"INDEX","ID","USER","FIRSTNAME","LASTNAME","LOCATION","EMAIL","LIKES_TIRAMISU","VALUE","SEARCH_FOR","TAGS","OBJECT"
"0","aca90d7d-e59e-4c91-b17","u.bjoerk","Uranus","Bjoerk","Aztlan","u.bjoerk@aztlan.com","false","66","Gianduiotto","[""shivering"",""yapping"",""shivering""]","{""active"":true,""score"":72.8,""status"":""DONE""}"
"1","459d167e-b905-44ec-a85","r.viklund","Rhea","Viklund","Muspelheim","r.viklund@muspelheim.com","false","25","Panna Cotta","[""stubborn"",""mega religious"",""tripping""]","{""active"":true,""score"":69.5,""status"":""PENDING""}"
"2","ff4c4746-dd96-405a-98e","e.wallin","Eros","Wallin","Laestrygon","e.wallin@laestrygon.it","","19","Taralli","[""high-end"",""underhanded"",""utterly arduous""]","{""active"":false,""score"":97.1,""status"":""NEW""}"
"3","018fb132-e63a-42d8-b3f","u.jansson","Uranus","Jansson","Kingdom of Reynes","u.jansson@kingdom-of-reynes.ch","true","7","Torrone","[""yapping"",""utterly arduous"",""out-of-control""]","{""active"":false,""score"":10.4,""status"":""DONE""}"
"4","45909aea-ff23-40c8-b36","p.samuelsson","Pluto","Samuelsson","Asphodel Meadows","p.samuelsson@asphodel-meadows.net","true","21","Tiramisu","[""ultra narrow-minded"",""absolutely arrogant"",""over-demanding""]","{""active"":false,""score"":57.2,""status"":""ARCHIVED""}"
"5","fe7164a1-3588-408d-bfa","a.wikstroem","Artemis","Wikstroem","Asphodel Meadows","a.wikstroem@asphodel-meadows.it","true","39","Nutella","[""infuriating"",""house-broken"",""superfluous""]","{""active"":true,""score"":11.9,""status"":""ARCHIVED""}"
"6","b62601a3-80b4-48f5-853","j.magnusson","Juno","Magnusson","Lyonesse","j.magnusson@lyonesse.me","true","23","Sanguinaccio Dolce","[""self-absorbed"",""tripping"",""zippy""]","{""active"":true,""score"":1.3,""status"":""ON HOLD""}"
"7","41a90b61-c9f7-4192-aa2","s.joensson","Saturn","Joensson","Camelot","s.joensson@camelot.it","false","10","Panna Cotta","[""absolutely arrogant"",""frisky"",""absolutely arrogant""]","{""active"":false,""score"":58.5,""status"":""NEW""}"
"9","e47d4979-bb18-418b-834","d.wallin","Dionysus","Wallin","Ram Setu","d.wallin@ram-setu.com","true","94","Pasticciotto","[""totally-at-the-ready"",""above hyperactive"",""fully-cruel-hearted""]","{""active"":true,""score"":30.2,""status"":""ON HOLD""}"
"10","6a59e76f-194d-4760-9b3","h.johansson","Hephaestus","Johansson","Asphodel Meadows","h.johansson@asphodel-meadows.net","","6","Panna Cotta","[""rebellious"",""ultra cuddly"",""underhanded""]","{""active"":false,""score"":70,""status"":""PENDING""}"
"11","2f85f73f-39ab-4dc5-85f","s.lundqvist","Sol","Lundqvist","Meropis","s.lundqvist@meropis.com","false","69","Pasticciotto","[""zippy"",""fiercely loyal"",""out-of-control""]","{""active"":true,""score"":83.6,""status"":""NEW""}"
Loading The File
In most cases, testdata files are shared between different use cases; therefore, we load the data in a global method or in our PFRTest class.
For our setup, we have a Globals class and initialize our Data source as a static field. We also create an Enumeration for the fields in our data, so we do not have to work with strings and can easily find out where a specific field is used by our IDE’s “Find Usages“ function.
public class Globals {
private static final String PACKAGE_DATA = "com.performetriks.performator.quickstart.data";
public static PFRDataSource DATA;
public enum DATAFIELDS {
INDEX, ID, USER, FIRSTNAME, LASTNAME, LOCATION, EMAIL
, LIKES_TIRAMISU, VALUE, SEARCH_FOR, OBJECT, TAGS;
}
public static void commonInitialization() {
// Load CSV
DATA = PFR.Data.newSourceCSV(PACKAGE_DATA, "testdata.csv", ",").build();
// Load JSON
// DATA = PFR.Data.newSourceJson(PACKAGE_DATA, "testdata.json").build();
}
} Initialize it in the Test Classes
We can now load our data source in any test by executing the Globals.commonInitialization() method:
public class PFRTestExample extends PFRTest {
public PFRTestExample() {
Globals.commonInitialization();
...
}
} Using the Data Source
In our PFRUsecase classes, we load one of the data records and access its fields using the enumeration constants we have defined. We can then use the retrieved values to put them into your test code.
One thing you see here that is rather uncommon in test data handling in load testing tools is that Performator supports loading Object and Array data out of the box. This is also true for CSV files, where you can define objects and arrays in JSON syntax (see example above).
public void execute() throws Throwable {
XRRecord record = Globals.DATA.next();
//============= Read Values =============
String user = record.getString(Globals.DATAFIELDS.USER);
int value = record.getInteger(Globals.DATAFIELDS.VALUE);
boolean likesTiramisu = record.getBoolean(Globals.DATAFIELDS.LIKES_TIRAMISU);
JsonObject objectData = record.getJsonObject(Globals.DATAFIELDS.OBJECT);
JsonArray tags = record.getJsonArray(Globals.DATAFIELDS.TAGS);
//============= Log Values ==============
logger.info("---------------");
logger.info("Record: " + record.toString());
logger.info("user: " + user);
logger.info("value: " + value);
logger.info("likesTiramisu: " + likesTiramisu);
logger.info("objectData: " + PFR.JSON.toJSON(objectData));
logger.info("tags: " + PFR.JSON.toJSON(tags)); Mass-Replacement with XRRecord.insert()
Data records provide an easy method to replace parameters in strings with the insert()-method. You can define parameters using the syntax ${fieldname}, and whatever value is assigned to that "fieldname" in your data record will be inserted in your string. This greatly simplifies the replacement of larger amounts of parameters in your test requests.
// data contains fields "searchTerm", "includeAll" and "limit"
XRRecord data = Globals.DATA.next();
data.add("customValue", "Forty-Two gallons of Tiramisu.")
PFRHttp.create("010_Search", url+"/app/search")
.POST()
.body(
data.insert("""
{
"search": "${searchTerm}"
, "includeAll": ${includeAll}
, "limit": ${limit}
, "custom": "${customValue}"
}
""")
)Conclusion
Loading test data files with Performator is well-structured and easy to achieve. Data can be retrieved as primitives, objects, and arrays and can be easily inserted at placeholders using the XRRecord.insert() method.
If you would like to learn more about Performator, feel free to reach out to our experts at Performetriks. We look forward to assisting you along your journey!
Happy Performance Engineering!




Comments