Home > Java, Jython, Programming and Development, Python > Writing Performance Tests in Grinder using a Framework

Writing Performance Tests in Grinder using a Framework

September 16th, 2009 vivin Leave a comment Go to comments

Before reading this tutorial (if you haven’t already), please took at look at my last two tutorials (Performance Testing using The Grinder and Anatomy of a Grinder test-script). In this tutorial I’ll talk about easily writing Grinder test-scripts using a framework I designed. As a disclaimer, I’d like to point out that I’m not a Python programmer and therefore certain things may not be very python-esque. If that’s the case, I apologize. My personal opinion is that this framework is especially useful (of course, since I wrote it ;) ) for web applications where you have a already have a lot of test data. In that case, you can simply record all your discrete tasks once and then construct different scenarios with them. But if you feel differently and have some constructive criticism, I do look forward to hearing from you! Also, if you’d like to try out the framework I’ve got a tarball and a zip file available for download on the very last page.

The motivation for a framework

Typically during performance-testing, you want to test different kinds of scenarios. These scenarios are made up of discrete tasks (also known as transactions), for example, consider a scenario where a user logs in, searches for a person, and then logs out. In this scenario, you have three separate tasks: logging in, searching for a person, and logging out. Using Grinder’s TCPProxy tool, you can easily record this scenario and you can also parameterize it. What happens when you want to record another scenario? Say, one that involves logging in, searching for a person, adding a new person, and logging out? Sure, you can go ahead and record that, and even parameterize it. But consider the fact that the new scenario is a super-set of the old scenario. It has three tasks in common with the first scenario. What if there was a way to create new scenarios from scratch, not by recording, but by using previously recorded tasks? This way you would only have to record information once, and after that, you can reuse it. To do this, we need to first understand how to identify tasks within a recorded Grinder test-script. Using this as our foundation, we can figure out how to pull out the tasks into discrete units, which we can later reuse.

The Relationship between Requests and Tasks

In the previous tutorial, I went over the anatomy of a recorded Grinder test-script. There, we saw that the script defines a bunch of requests, and then has methods that correspond to each recorded page. In each method, the script executes a bunch of requests. Initially you might think that we would simply pull out these individual methods. However, that is not the case. A single task can involve more than one recorded page. For example, the act of logging into the app involves accessing the login page, then logging in, and then hitting whatever page the login process drops you into. This involves (at the very least) three pages. In fact, the relationship between requests, pages, and tasks looks something like this:

The relation between requests and tasks

The relation between requests and tasks

Here, you can see that a set of unique requests belong to a recorded page, and then a set of recorded pages belong to a unique task. Finally, a set of tasks belong to a scenario. What we want to do is pull out the individual tasks so that we can reuse them to create different scenarios. To do this, we will still be using Grinder’s TCPProxy tool (at least to record the scenario), but our resulting script will not look like the typical Grinder test-script. It will, instead, conform to the testing framework.

Related Posts

  1. September 18th, 2009 at 07:56 | #1

    Dear vivin

    Happy onam to you. we are a group of students from cochin who are currently building a web portal on kerala. in which we wish to include a kerala blog roll with links to blogs maintained by malayali’s or blogs on kerala.

    you could find our site here: http://enchantingkerala.org

    the site is currently being constructed and will be finished by 1st of sep 2009.

    we wish to include your blog located here

    http://vivin.net/

    we’ll also have a feed fetcher which updates the recently updated blogs from among the listed blogs thus generating traffic to your recently posted entries.

    If you are interested in listing your site in our blog roll; kindly include a link to our site in your blog in the prescribed format and send us a reply to enchantingkerala.org@gmail.com and we’ll add your blog immediatly.

    pls use the following format to link to us

    Kerala

    hoping to hear from you soon.

    warm regards

    Biby Cletus

  2. kamalakar
    September 25th, 2009 at 03:05 | #2

    Hi,

    I was evaluating Grinder tool..
    If the Tester Runner method is big . i mean if Testrunner class exceeds certain length then i am unable to run the test and the tool is throwing following exception…

    As a workaround we can divide the script into more than once script but the problem here is how can i call one script in other…?? could you help me..

    Thanks In Advance
    Kamalakar

  3. September 25th, 2009 at 09:57 | #3

    @kamalakar
    That’s strange – I’ve never run into that issue before. What is the exception you’re getting? You might consider putting each one into a class of its own and since they’re callable, you might be able to call them split them into separate classes and then write a wrapper that calls them? I think you’ll probably have better luck on the grinder-use mailing list – have you asked your question on there?

  4. October 9th, 2009 at 06:13 | #4

    I have read some of your posts and find your style of writing very interersting. Salute you on how neatly the articles are presented.

  5. October 9th, 2009 at 08:08 | #5

    @Avinash Mangipudi
    Thanks Avinash!

  6. Mukes Nair
    November 3rd, 2009 at 18:45 | #6

    I get following error for all import statements of my framework scripts (ImportError: no module named sci).

    This goes away if I compile all my dependent scripts and add jar to classpath of agent. But then I cant parameterize methods in those dependent scripts as they are already classes.

    How do I solve this?

    I tried settting PYTHONPATH, passing python.home in grinder.properties. nothing seems to work for me.

  7. Mukesh Nair
    November 3rd, 2009 at 20:17 | #7

    I found out the issue. I had to put my script sources folder in Jython registry file.@Mukes Nair

  8. November 3rd, 2009 at 21:38 | #8

    @Mukesh Nair
    Sorry I didn’t get back to you sooner. I didn’t check my email until now! I’m glad that you were able to fix the issue. I wasn’t clear on your initial problem though. Did you have problems importing the actual scripts (that you created using the framework) themselves?

  9. kamalakar
    November 5th, 2009 at 02:50 | #9

    Hi ,

    I am trying to use torque framework , facing problem with the scenario class.
    As scenario class showing error at following code.

    ” urlDict = property(getUrlDict, setUrlDict)” because my eclipse is unable to find property import.
    i have jython 2.1 with me. it seems the property import is not available on the jython 2.1

    Could you please help me wat might be the problem..?

    Thanks
    Kamalakar

  10. kamalakar
    November 5th, 2009 at 06:25 | #10

    I need a a way to share some data between tasks..
    How can i do this?
    Do we need to add other prop in the scenario class

  11. kamalakar
    November 5th, 2009 at 06:39 | #11

    @kamalakar
    Hi
    i am able to fix this error.. the problem is with my environment
    Actually i am using GrinderStone its eclipse plugin for Grinder Tool.

    Thanks
    Kamalakar

  12. November 5th, 2009 at 09:50 | #12

    @kamalakar
    Sorry I wasn’t able to get to you sooner! Glad you were able to fix the issue – it did sound like an environment problem.

    What data are you trying to share between tasks?Tasks are discrete entities that should have nothing in common with each other. Hence, they shouldn’t be sharing state-data. However, they can share input data (from a common resource – like a file).

  13. kamalakar
    November 5th, 2009 at 23:30 | #13

    suppose consider following usecase:
    1.login is one task: after user got logged in we will get a userid .
    2.second task :
    — Here the request in this task need the userid from the previous task.

    So, we need a global parameters that can be accessible throughout the scenario.
    Could you suggest me how we can achieve . I have one approach in my mind.
    — Add another param like ‘urlDict’ in the Scenario Class . what do you say.?

    Thanks
    Kamalakar

  14. November 6th, 2009 at 09:49 | #14

    @kamalakar
    In this case I’d parameterize the usernames and userId’s and link them to the thread number. You’d have to do this even if you were testing a pure Grinder-script that didn’t use the framework.

  15. Mukesh Nair
    November 19th, 2009 at 09:27 | #15

    I have another issue to resolve. Any help will be appreciated. Lets take a scenario of a billing counter like of BestBuy. The associate will log in and load the billing screen. Then he will enter items and print receipt for a customer. Then for the next customer and so on. Here I have classified tasks in the scenario as follows
    1. Login Task
    2. Load Billing Screen Task
    3. Enter Items Task
    4.Print receipt Task
    5. Logout task

    Now in the run I want to run tasks 1, 2 and 5 only once. I want to repeat 3 and 4.

    How do I do this?

    Thanks
    Mukesh

  16. November 19th, 2009 at 10:26 | #16

    @Mukesh Nair

    I’m assuming that you know how many times you need to repeat tasks 3 and 4? You can simply create n “Enter Items” tasks and n “Print receipt” tasks (through a loop perhaps, and maybe have an array for each, for example enterItemsTaskArray and printReceiptTaskArray).

    When you build your scenario, you add Task 1 and 5, then iterate through both arrays at the same time and them to the scenario (so you’d add one “Enter items” task and then one “Print receipt” task). After you’re done you add Task 5.

  17. Mukesh Nair
    November 19th, 2009 at 11:03 | #17

    Actually I don’t know how many times I will be repeating 3 and 4. I found another way to tackle the issue. Like this.

    class TestRunner:
    def __init__(self):
    log(“initializing scenario %s” % self.__class__.__name__)
    myScenario.initialize()

    def __call__(self):
    log(“running scenario %s” % self.__class__.__name__)
    myScenario.run()

    def __del__(self):
    log(“finalizing scenario %s” % self.__class__.__name__)
    myScenario.finalize()

  18. Mukesh Nair
    November 19th, 2009 at 11:13 | #18

    @Mukesh Nair

    so my scenario will look like:

    myScenario.addInitTask(loginTask)
    myScenario.addInitTask(loadPackScreenTask)
    myScenario.addTask(scanShipmentTask)
    myScenario.addTask(newCaseTask)
    myScenario.addTask(scanItemTask)
    myScenario.addTask(closeContainerTask)
    myScenario.addFinalTask(logoutTask)

    I think this should solve my current issue.

  19. November 19th, 2009 at 11:26 | #19

    @Mukesh Nair
    That would work as well. I’m assuming that you’re using meta-programming and extending the Scenario class at runtime?

    Even if you didn’t know how many times you’d be repeating it, as in let’s say that it will be repeated a random number of times, you can add a random number of items to the arrays. But I do like your solution. It’s a little more elegant.

  20. Mukesh Nair
    November 20th, 2009 at 09:09 | #20

    I have a problem of data getting mixed up between threads. Look at the log below.

    initially thread-1 had value 100000019. Once thread -0 got the value 100000024, i printed out values again. Now both threads have the same value.

    I decompiled one of the class files and I found that self is declared as:
    static final py self;

    Did you face similar issue?

    11/20/09 10:56:09 AM (thread 1 run 0 test 101): self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"] 100000019
    11/20/09 10:56:09 AM (thread 0 run 0 test 101): suggestedShipments ['100000019', '100000024']
    11/20/09 10:56:09 AM (thread 0 run 0 test 101): self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"] 100000024
    11/20/09 10:56:09 AM (thread 1 run 0 test 4201): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 365 bytes
    11/20/09 10:56:09 AM (thread 1 run 0 test 4201): sleeping for 29 ms
    11/20/09 10:56:09 AM (thread 0 run 0 test 4201): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 919 bytes
    11/20/09 10:56:09 AM (thread 0 run 0 test 4201): sleeping for 31 ms
    11/20/09 10:56:09 AM (thread 1 run 0 test 4301): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 244 bytes
    11/20/09 10:56:09 AM (thread 1 run 0 test 4301): sleeping for 34 ms
    11/20/09 10:56:09 AM (thread 0 run 0 test 4301): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 244 bytes
    11/20/09 10:56:09 AM (thread 0 run 0 test 4301): sleeping for 30 ms
    11/20/09 10:56:09 AM (thread 1 run 0 test 4401): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 919 bytes
    11/20/09 10:56:09 AM (thread 0 run 0 test 4401): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 919 bytes
    11/20/09 10:56:09 AM (thread 1 run 0 test 4401): sleeping for 63 ms
    11/20/09 10:56:09 AM (thread 0 run 0 test 4401): sleeping for 60 ms
    11/20/09 10:56:09 AM (thread 1 run 0 test 4501): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 341 bytes
    11/20/09 10:56:09 AM (thread 1 run 0 test 4600): self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"] 100000024
    11/20/09 10:56:09 AM (thread 0 run 0 test 4501): http://10.10.20.80:7099/smcfs/console/exuipack.exui -> 200 OK, 341 bytes
    11/20/09 10:56:09 AM (thread 0 run 0 test 4600): self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"] 100000024

  21. November 20th, 2009 at 09:22 | #21

    @Mukesh Nair
    How are you parameterizing the data? Are you doing it based on the thread number?

  22. Mukesh Nair
    November 20th, 2009 at 09:37 | #22

    @vivin
    am setting data in the task itself using one method call to a utility script, like below

    def SelectStationAndScanShipment2(self):
    “”"Selecting station and scanning shipment POST exuipack.exui (request 201).”"”
    self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"] = self.StringUtil.getAPickedShipmentNumber(self.parameters["SelectStationAndScanShipment2"]["201"]["OrganizationCode"])
    self.log(“self.parameters[\"SelectStationAndScanShipment2\"][\"201\"][\"BarcodeData\"] %s” % self.parameters["SelectStationAndScanShipment2"]["201"]["BarcodeData"])

  23. November 20th, 2009 at 10:06 | #23

    @Mukesh Nair
    I recommend not modifying the actual task. What you want to do is create a parameterizing method that sets the values in the parameters dictionary for you. Check out my examples in this post.

  24. Mukesh Nair
    November 23rd, 2009 at 11:42 | #24

    @vivin
    i wanna show you a grinder xml which is not producing correct url in the task created by the perl script. I directly created grinder script using proxy. I can see a lot of arguements witht he url. But when it is converted using using the perl script into a task, all the arguements are missing. let me know where to send the related files so that you can take a look. I haven’t looked into perl yet. So I thought you could resolve it faster.

  25. November 23rd, 2009 at 13:46 | #25

    @Mukesh Nair
    Is it a POST or a GET? I can take a look at it. I DM’ed you my email on twitter. So just send me the XML and I’ll take a look at it.

  26. vicky
    March 4th, 2010 at 04:42 | #26

    Hi Vivin,

    Here i need your help. I am working in web application and i wanna to test the load of the application. For this i am using the grinder tool and under stand all the things except properties file of grinder framework. Is it possible to run more than one script or divide the scripts in threads on same time. Here i can explain you through example. Suppose we have 4 scripts and want 4 scripts are equally divided in 100 user’s. is it possible?

  27. March 4th, 2010 at 12:58 | #27

    @vicky
    I looked around for ways to run multiple scripts and I ran into this post here that says you can do it by making each script as a separate property and not space delimited. So what you need to do is divide the load (and select a user) based on the thread number. Script 1 will use user1-user25 (if the thread number is between 1 and 25), script 2 will use user26-user50 (if the thread number is between 26 and 50). This basically means that you need to have 100 threads.

  1. December 7th, 2009 at 11:35 | #1