Skip to content

Rough Book

random musings

Menu
  • About Me
  • Contact
  • Projects
    • bAdkOde
    • CherryBlossom
    • FXCalendar
    • Sulekha
Menu

Performance testing using The Grinder

Posted on July 27, 2009September 16, 2009 by vivin

About a month ago at work, I was trying out a bunch of different performance-testing tools to figure out which one to use to performance-test our software. I ended up discovering a tool called The Grinder which uses Jython to build performance-testing scripts that you can then use to test your application. I even built a little framework in Jython to make the test scripts more modular and reusable. Over the next few days I'll be publishing three articles (this one included) that talk about Grinder.

Introduction

In this article I'll go over installing and setting up Grinder. I am not going to go into too many details since my aim is to provide information that will enable you to have Grinder up and running quickly. If you want more information, you can look at the rather thorough Grinder User Guide. At the end of this guide, you'll know how to install and set up grinder, record a test, modify (and/or parameterize) it, start running it through the console, and record data from the test. Note: The instructions in this guide relate to a Linux environment. You can run Grinder in Windows; the set-up is not much different. The only differences will be in installation locations and shell scripts. If you want more information about setting up Grinder in a Windows environment, please take a look here.

Downloading everything you need

First you need to download Grinder. You can get it from here. You should only need to download the binary distribution and not the source distribution (unless you're planning on extending Grinder). Unzip the installation file to somewhere convenient. I extracted the file to /usr/local/grinder. I recommend chown'ing the installation directory to yourusername:yourusername. Make sure that your version of the JDK is greater than or equal to 1.4, otherwise Grinder won't work! The second thing you need to install is Jython. Note that this is not absolutely necessary, but I found it useful to have around when I was trying to parameterize the test-scripts, and I needed to test something out first. I recommend not using the Jython that comes with Ubuntu. Instead, download and install Jython 2.2.1 from here (there are issues with Jython 2.5 and Grinder).

Setting up Grinder

For demonstration purposes, I'm going to show you how to set up Grinder with a local grinder.properties file, with a local console and a local agent running workers. To get an explanation as to what these terms mean, take a look at the picture below that I shamelessly stole from the Grinder documentation page:

Processes

As you can see, you essentially have a console that sits on top of everything. It acts like a controller, pushing out scenarios and test cases (scripts) to agents that are running on load-injection machines. Each agent is in charge of one or more workers that inject load into the system you are testing. The workers also report data back to the console. The console aggregates all the data it receives from the workers and presents it to you. This is the ideal scenario that you would use for actual performance testing, however like I said before, I'm not going to concentrate on that - I'm just going to show you how to quickly set up Grinder and run a performance test on your local machine. If you want to look into distributing scenarios and tests across different agents, you can take a look here.

The first thing you want to do is set up a grinder.properties file. I put this file into the root directory of the checked out app (so basically, wherever you checked out your app). It's not necessary to put it there; you can put it wherever you want. So open up your favorite text editor, and create the following file:

[sourcecode]
# Tell Grinder how many processes, threads and runs (iterations) we have in this scenario
grinder.processes = 1
grinder.threads = 5
grinder.runs = 10

# grinder.jvm.arguments sends arguments to the JVM. The reason I have the following value
# is to prevent an error (Jython complains about caches) from popping up when you start
# a test. Jython caches imports and so you need to let it know where it can store the imports.
# Though it's not strictly necessary, you do need it if you are performing wildcard imports
# (like java.util.*)
grinder.jvm.arguments = -Dpython.cachedir=/tmp/mycache
grinder.script = grinder.py

# Here you set up the arguments that tells the agent where the console lives.
grinder.hostID = dauntless
grinder.consoleHost = dauntless
grinder.consolePort = 6372
grinder.useConsole = true
grinder.reportToConsole.interval = 500

# sleepTime is a synonym for "think time". You don't want the load-generator hammering away at the
# app one request after another with a uniform interval. sleepTime introduces time variations between
# different requests
grinder.initialSleepTime = 0
grinder.sleepTimeFactor = 1
grinder.sleepTimeVariation = 0.2

# These properties are used to configure the behavior of Grinder with respect to logging
grinder.logProcessStreams = true
grinder.reportTimesToConsole = true
grinder.debug.singleProcess = false
grinder.useNanoTime = false
[/sourcecode]

If you want more information regarding the grinder.properties file, take a look at this page.

Alright, now that you have the grinder.properties created, you also need a few shell-scripts to easily start and stop the console, agent, and TCP Proxy. You will have to change the values of GRINDERPATH, GRINDERPROPERTIES, and JAVA_HOME to values that make sense on your system.

[sourcecode language="bash"]
#!/bin/bash
GRINDERPATH=/usr/local/grinder/grinder-3.2
GRINDERPROPERTIES=./grinder.properties
CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH
JAVA_HOME=/usr/local/java/6u13
PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH PATH GRINDERPROPERTIES
[/sourcecode]

[sourcecode language="bash"]
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.TCPProxy -console -http > grinder.py
[/sourcecode]

[sourcecode language="bash"]
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Console
[/sourcecode]

[sourcecode language="bash"]
#!/bin/bash
. ./setGrinderEnv.sh
java -cp $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES
[/sourcecode]

Creating a Test

Before you start performance testing, you need to have a test. Grinder test scripts are written in the Jython language. You can write your tests from scratch if you wish (it can be useful if you're trying to test things like XML-RPC), but more often than not you will be using Grinder's proxy tool to record a usage scenario and generate a test script. The tool that Grinder comes with is called TCP Proxy. It sits between the browser and the server and records requests and responses that move between the browser and the server. By itself, it simply echoes whatever goes through it (in that sense, it makes a pretty good debugging tool), but we want it to generate Jython test scripts. Note: TCPProxy actually uses XSLT to translate the header information into a Jython script. This is pretty neat, actually because you can use XSLT to translate the code to Groovy, or even JRuby.

TCPProxy

First, you need to start up TCP Proxy. Simply run startProxy.sh and you should be good to go. A little window will pop-up letting you know that the proxy is running. There's a textbox in the window that lets you insert comments too. Once you the proxy is started up, you need to configure your browser to use the proxy at localhost:8001. Now access the app you want to test, in your browser. Log in and do whatever it is you need to do (search for a contact, add a contact, send and email, etc.). In the scenario I created, I logged in, searched for contacts, added a new contact, searched for more contacts, and then logged out. Note: If you get an alert box that complains about security certificates, you can add an exception in Firefox by going to Edit->Preferences->Advanced->Encryption Tab->View Certificates->Add Exception. Put in the URL to your server and Firefox should grab the certificate and add an exception.

Once you're done recording the test, press the Stop button in TCPProxy.

WARNING: When you're running TCPProxy, make sure that you have no other tabs open! Also make sure that you have no plugins trying to phone home (through the browser). TCPProxy intercepts every communication that your browser makes so you don't want to pollute your results with irrelevant requests (I think it's possible to filter the data using the domain name, but I haven't looked into how to do it yet)

Parameterizing the test

There are a few tests where you would like to supply data (for example, when adding a contact). In the context of performance-testing, this is called parameterizing. Since Grinder uses Jython, it's actually pretty easy to parameterize a test. First, let's take a look at a TCPProxy-generated Jython-script. I'm only going to reproduce parts of the script since the entire script itself is rather large (however, if you want to look at the entire script, you can download it):

[sourcecode language="python"]
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair
connectionDefaults = HTTPPluginControl.getConnectionDefaults()
httpUtilities = HTTPPluginControl.getHTTPUtilities()

# To use a proxy server, uncomment the next line and set the host and port.
# connectionDefaults.setProxyServer("localhost", 8001)

# These definitions at the top level of the file are evaluated once,
# when the worker process is started.

connectionDefaults.defaultHeaders = \
( NVPair('Accept-Language', 'en-us,en;q=0.5'),
NVPair('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'),
NVPair('Accept-Encoding', 'gzip,deflate'),
NVPair('User-Agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11'), )

headers0= \
( NVPair('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
NVPair('Referer', 'https://local.infusiontest.com:8443/?msg=You%27ve+been+logged+out+-+thanks+for+playing%21&notification=You%27ve+been+logged+out+-+thanks+for+playing%21'), )

headers1= \
( NVPair('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
NVPair('Referer', 'https://local.infusiontest.com:8443/Admin/home.jsp'), )

...
...

headers9= \
( NVPair('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'),
NVPair('Referer', 'https://local.infusiontest.com:8443/Admin/home.jsp?revertCalUser=true'),
NVPair('Cache-Control', 'no-cache'), )

headers10= \
( NVPair('Accept', 'image/png,image/*;q=0.8,*/*;q=0.5'),
NVPair('Referer', 'https://local.infusiontest.com:8443/?msg=You%27ve+been+logged+out+-+thanks+for+playing%21&notification=You%27ve+been+logged+out+-+thanks+for+playing%21'), )

url0 = 'https://local.infusiontest.com:8443'

# Create an HTTPRequest for each request, then replace the
# reference to the HTTPRequest with an instrumented version.
# You can access the unadorned instance using request101.__target__.
request101 = HTTPRequest(url=url0, headers=headers0)
request101 = Test(101, 'POST processLogin.jsp').wrap(request101)

request102 = HTTPRequest(url=url0, headers=headers0)
request102 = Test(102, 'GET home.jsp').wrap(request102)

request201 = HTTPRequest(url=url0, headers=headers1)
request201 = Test(201, 'GET popUpTask.jsp').wrap(request201)

...
...

request3004 = HTTPRequest(url=url0, headers=headers0)
request3004 = Test(3004, 'GET defaultLogin.jsp').wrap(request3004)

request3005 = HTTPRequest(url=url0, headers=headers0)
request3005 = Test(3005, 'GET index.jsp').wrap(request3005)

class TestRunner:
"""A TestRunner instance is created for each worker thread."""

# A method for each recorded page.
def page1(self):
"""POST processLogin.jsp (requests 101-102)."""

# Expecting 302 'Moved Temporarily'
result = request101.POST('/login/processLogin.jsp',
( NVPair('username', 'myusername'),
NVPair('password', '[email protected]'),
NVPair('Login', 'Login'), ),
( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))

grinder.sleep(49)
request102.GET('/Admin/home.jsp')

return result

def page2(self):
"""GET popUpTask.jsp (request 201)."""
result = request201.GET('/files/popUpTask.jsp' +
'?0.6697580414936783')

return result

def page3(self):
"""POST calendarBackend.jsp (request 301)."""
result = request301.POST('/Calendar/calendarBackend.jsp',
( NVPair('calType', 'Day'),
NVPair('userId', '1'),
NVPair('calDate', '17'),
NVPair('calMonth', '5'),
NVPair('calYear', '2009'),
NVPair('weekStartDate', '-1'),
NVPair('weekStartMonth', '-1'),
NVPair('weekStartYear', '-1'),
NVPair('weekEndDate', '-1'),
NVPair('weekEndMonth', '-1'),
NVPair('weekEndYear', '-1'), ),
( NVPair('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'), ))

return result

...
...

def page18(self):
"""POST processContact.jsp (requests 1801-1802)."""

# Expecting 302 'Moved Temporarily'
result = request1801.POST('/Contact/processContact.jsp',
( NVPair('addingFormId', '0'),
NVPair('tabs_sel', 'main'),
NVPair('Contact0Id', '0'),
NVPair('Contact0FirstName', "Mikey"),
NVPair('Contact0LastName', "Mike"),
...
...
NVPair('Contact0Website', ''),
NVPair('Contact0Email', "[email protected]"),
NVPair('email_Contact0Email', '1'),
...
...
NVPair('view', 'add'),
NVPair('Save', 'Save'), ),
( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
self.token_view = \
httpUtilities.valueFromLocationURI('view') # 'edit'
self.token_tabs_sel = \
httpUtilities.valueFromLocationURI('tabs_sel') # 'main'
self.token_msg = \
httpUtilities.valueFromLocationURI('msg') # 'Person Added'

grinder.sleep(46)
request1802.GET('/Contact/manageContact.jsp' +
'?view=' +
self.token_view +
'&tabs_sel=' +
self.token_tabs_sel +
'&msg=' +
self.token_msg)

return result

...
...

def __call__(self):
"""This method is called for every run performed by the worker thread."""
self.page1() # POST processLogin.jsp (requests 101-102)

grinder.sleep(350)
self.page2() # GET popUpTask.jsp (request 201)

grinder.sleep(385)
self.page3() # POST calendarBackend.jsp (request 301)

...
...

grinder.sleep(407)
self.page29() # POST calendarBackend.jsp (request 2901)

grinder.sleep(3661)
self.page30() # GET logout.jsp (requests 3001-3005)

def instrumentMethod(test, method_name, c=TestRunner):
"""Instrument a method with the given Test."""
unadorned = getattr(c, method_name)
import new
method = new.instancemethod(test.wrap(unadorned), None, c)
setattr(c, method_name, method)

# Replace each method with an instrumented version.
# You can call the unadorned method using self.page1.__target__().
instrumentMethod(Test(100, 'Page 1'), 'page1')
instrumentMethod(Test(200, 'Page 2'), 'page2')
instrumentMethod(Test(300, 'Page 3'), 'page3')
...
...
instrumentMethod(Test(2900, 'Page 29'), 'page29')
instrumentMethod(Test(3000, 'Page 30'), 'page30')
[/sourcecode]

From the above listing, you can get a general idea of the anatomy of a test script. Of course, there's a little bit more that goes into this, so if you want a detailed description, take a look at this page (in my next article, I'll go into more detail about the anatomy of a recorded script). By itself, this script is 95% useful. However, to make it exactly what we need, we need to parameterize certain areas. The first section we need to parameterize is where we add a new contact (look at the page18 method). Right now, it inserts a contact called "Mikey Mike" with the email "[email protected]". That's not particularly useful if we're running this test a bunch of times. We want to parameterize this value. What we're going to do is create a random first name, last name, and email address, and use those values in the request. Here's how we do it (once again, I'm only showing the pertinent sections of the code i.e., the sections that have changed):

[sourcecode language="python"]
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

# We need to import java.util.Random for random number generation
from java.util import Random

...
...

def page18(self):
"""POST processContact.jsp (requests 1801-1802)."""

chars = [ "b", "c", "d", "f", "g", "h", "j", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z", "th", "dh", "gh", "ph", "ch", "st", "sw", "sp", "kh" ];
vowels = [ "a", "e", "i", "o", "u" ];
r = Random();

fname = "";
lname = "";

for i in range(0, r.nextInt(3) + 2):
fname += chars[r.nextInt(len(chars))] + vowels[r.nextInt(len(vowels))];

for i in range(0, r.nextInt(3) + 2):
lname += chars[r.nextInt(len(chars))] + vowels[r.nextInt(len(vowels))];

# Expecting 302 'Moved Temporarily'
result = request1801.POST('/Contact/processContact.jsp',
( NVPair('addingFormId', '0'),
NVPair('tabs_sel', 'main'),
NVPair('Contact0Id', '0'),
NVPair('Contact0FirstName', fname.capitalize()),
NVPair('Contact0LastName', lname.capitalize()),
...
...
NVPair('Contact0Website', ''),
NVPair('Contact0Email', fname + "." + lname + "@" + lname + ".com"),
NVPair('email_Contact0Email', '1'),
...
...
NVPair('Save', 'Save'), ),
( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))
self.token_view = \
httpUtilities.valueFromLocationURI('view') # 'edit'
self.token_tabs_sel = \
httpUtilities.valueFromLocationURI('tabs_sel') # 'main'
self.token_msg = \
httpUtilities.valueFromLocationURI('msg') # 'Person Added'

grinder.sleep(46)
request1802.GET('/Contact/manageContact.jsp' +
'?view=' +
self.token_view +
'&tabs_sel=' +
self.token_tabs_sel +
'&msg=' +
self.token_msg)

return result
[/sourcecode]

As you can see, we've now parameterized the first name and last name values that go into processContact.jsp. There is still one more thing we need to parameterize. If you go back and take a look at grinder.properties, you can see that I set grinder.threads to 5. This means that there are going to be 5 threads running. We don't want the same user logging in more than once (our app doesn't allow it). So what we also need to do is parameterize user logins:

[sourcecode language="python"]
# The Grinder 3.2
# HTTP script recorded by TCPProxy at Jun 17, 2009 4:57:02 PM

from java.util import Random
from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair
connectionDefaults = HTTPPluginControl.getConnectionDefaults()
httpUtilities = HTTPPluginControl.getHTTPUtilities()

# To use a proxy server, uncomment the next line and set the host and port.
# connectionDefaults.setProxyServer("localhost", 8001)

# These definitions at the top level of the file are evaluated once,
# when the worker process is started.

# Setting up an array of dictionaries (i.e., hash/map/associative-array) of username and password name-value pairs
loginCredentials = [{"username":"vivin", "password":"[email protected]"},
{"username":"jimbo", "password":"[email protected]"},
{"username":"hippy", "password":"[email protected]"},
{"username":"flippy", "password":"[email protected]"},
{"username":"batman", "password":"[email protected]"}];

...
...

class TestRunner:
"""A TestRunner instance is created for each worker thread."""

# A method for each recorded page.
def page1(self):
"""POST processLogin.jsp (requests 101-102)."""

loginCredential = loginCredentials[grinder.threadNumber];
username = loginCredential["username"];
password = loginCredential["password"];
# Expecting 302 'Moved Temporarily'
result = request101.POST('/login/processLogin.jsp',
( NVPair('username', username),
NVPair('password', password),
NVPair('Login', 'Login'), ),
( NVPair('Content-Type', 'application/x-www-form-urlencoded'), ))

grinder.sleep(49)
request102.GET('/Admin/home.jsp')

return result
[/sourcecode]

To parameterize logins, I created an array of dictionaries that store username and password combinations. Then, based on the thread number (which is zero-based and which I can access via grinder.threadNumber) I select the appropriate username and password combination, to log in.

Running the tests

Now we can actually start running the tests and recording data! The first thing you need to do is start up your app. Then, you need to start the Grinder console. Use the startConsole.sh shell-script and start up the Grinder console. After that, use startAgent.sh to start up the agent. You should see something like this in your shell:

[sourcecode]
[email protected] ~/Projects/crm-1.16.0.x
$ startConsole.sh &
[1] 16180

[email protected] ~/Projects/crm-1.16.0.x
$ startAgent.sh
6/18/09 4:43:19 PM (agent): The Grinder 3.2
6/18/09 4:43:19 PM (agent): connected to console at dauntless/127.0.1.1:6372
6/18/09 4:43:19 PM (agent): waiting for console signal
[/sourcecode]

If you see this, it means that your agent started up, and was able to talk to the Grinder console. In the Grinder console, there are four icons in the top-left corner of the window. If you mouse-over the leftmost icon, you will see a tooltip that says "Start the worker processes". Click that button. A dialog box will pop up saying that you haven't selected a properties file and that the worker processes will run the script set in the agent's properties file. This is fine, since you are running Grinder locally and not distributing a file. Click Ok. You should now see something like this in your shell:

[sourcecode]
[email protected] ~/Projects/crm-1.16.0.x
$ startConsole.sh &
[1] 16180

[email protected] ~/Projects/crm-1.16.0.x
$ startAgent.sh
6/18/09 4:50:12 PM (agent): The Grinder 3.2
6/18/09 4:50:12 PM (agent): connected to console at dauntless/127.0.1.1:6372
6/18/09 4:50:12 PM (agent): waiting for console signal
6/18/09 4:51:48 PM (agent): received a start message
6/18/09 4:51:48 PM (agent): Worker process command line: java '-Dpython.cachedir=/tmp/mycache' -classpath '/usr/local/grinder/grinder-3.2/lib/grinder.jar:' net.grinder.engine.process.WorkerProcessEntryPoint
6/18/09 4:51:48 PM (agent): worker dauntless-0 started
*sys-package-mgr*: processing new jar, '/usr/local/grinder/grinder-3.2/lib/grinder.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/resources.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/rt.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/jsse.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/jce.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/charsets.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/ext/sunpkcs11.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/ext/sunjce_provider.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/ext/localedata.jar'
*sys-package-mgr*: processing new jar, '/usr/local/java/6u13/jre/lib/ext/dnsns.jar'
*sys-package-mgr*: processing new jar, '/usr/local/grinder/grinder-3.2/lib/jython.jar'
6/18/09 4:51:53 PM (process dauntless-0): starting threads
[/sourcecode]

If you check back into the Grinder console, you can see that it is grabbing data from the agent. Now just sit back and let the test run. Once the test is complete, hit the stop button and exit out of console. If you go to the directory from where you started up the agent, you will see three log files: data_-N.log, error_-N.log, and out_-N.log. data_-N.log is the file that contains all the data gathered (like mean response time, transactions per second, etc.). It is a comma-separated-values file and you can open it up in Excel or Openoffice Calc. error_-N.log contains any errors encountered while running the test, and out_-N.log contains logging data from the script file. You are now in a position to analyze data from the log files. WARNING: I recommend NOT using the version of Java that comes with Ubuntu (GCJ/IcedTea or whatever). Instead, use Sun's Java6. I ran into a whole bunch of problems and wasted an entire day (you can view the thread describing the problem here).

Analyzing Grinder-Generated Data

Grinder spits out raw data. So it's not that user-friendly when compared to, say, WebLoad. This makes sense when you consider the fact that Grinder is geared towards developers. Grinder says "Here is the data I collected, do whatever you want with it". If you want a quick overview of your performance data, there is a tool called GrinderAnalyzer that you can download from here. Usage is pretty straightfoward (check out the documentation). Extract it to your user home directory. Then run the shell script (run.sh) in the bin directory while providing it your data_-N.log and out_-N.log files. GrinderAnalyzer will generate some pretty graphs and some pretty tables for you to view. Your other option is to examine the data on your own and generate graphs. This, while a bit more involved, gives you the freedom to display the data in a manner you see fit.

Conclusion

Grinder is a pretty flexible and powerful performance-testing tool. I'm hoping that after this tutorial you have enough knowledge to go out and start creating and running tests of your own. Like I mentioned before, this guide is by no meand comprehensive. It's simply something to get you started. If you want more information, I highly recommend looking at the excellent Grinder Documentation.

Stay tuned for the next article, where I'll be talking about the anatomy of a recorded Grinder test-script...

25 thoughts on “Performance testing using The Grinder”

  1. Hiral says:
    July 26, 2001 at 6:07 pm

    Hi Vivin,

    Its really great article. Thank you so much for sharing these details. I just started exploring grinder for performance testing. Your article is really great help for parametrization in test script.

    Reply
  2. Pingback: Blink 182 Blink 182 Rock Music CD Review | Rock Music 4 ALL
  3. Pingback: Rough Book » Anatomy of Grinder test-script
  4. Pingback: Rough Book » Writing Performance Tests in Grinder using a Framework
  5. Pingback: Trapeze Medusa Rock Music CD Review | Rock Music 4 ALL
  6. Chris says:
    November 18, 2009 at 11:20 am

    Hi – thanks very much for this – am currently evaluating grinder and this was by far the best quickstart guide I could find!

    Quick question if you have a moment – I just want to confirm, but unlike other ‘commercial’ load test tool offerings that can dynamically parse a standard html page response then retrieve the page resources automatically (jpg, css etc), these all have to written as individual requests in the grinder so if the page images change then these have to be re-recorded or at least re-scripted? I believe the answer is yes, this is the case and if you wanted to do this you’d have to write custom scripting to parse the HTTPResponse text, but just want to get your thoughts on this – thanks!

    Reply
  7. vivin says:
    November 18, 2009 at 1:00 pm

    @Chris
    Hi Chris,

    Thanks! I’m glad you like the guide!

    If I understand your question, you’re wondering if you’d have to re-record or re-script the test if your resources change. The answer is ‘yes’. But I would think that you’d have to do the same thing for commercial load-testing software as well (if page names change, for example). But you’re correct about parsing the response. A Grinder script simply hits a URL (and may send POST or GET data) and doesn’t go through the response text. I think the difference is in the way Grinder is structured; it has two independent phases. In the recording phase, Grinder records everything, including page resources that are accessed when you hit a page. It seems that in some commercial load-testers, those phases are intertwined. So you simply provide a URL and then it parses the response that it gets. This would seem to remove the dependency on resources.

    I think it is possible to extend Grinder so that it displays the same behavior.

    Reply
  8. Chris says:
    November 21, 2009 at 2:05 pm

    Great – thanks for the response! Thought that was the case, and thanks for confirming. If I get a chance to extend to allow this behaviour, I’ll be sure to let you know.

    Chris

    Reply
  9. vishal sharma says:
    February 11, 2010 at 2:19 am

    Hi,
    nice article to go through.
    i just started exploring grinder 3.3
    i tried parameterizing my user login application as discussed above.
    however i am not getting the desired output. Here is my snippet
    loginCredentials = [{“username”:”user1″, “password”:”123″},{“username”:”user2″, “password”:”123″}];

    def page6(self):
    “””POST Login (request 601).”””
    loginCredential = loginCredentials[grinder.threadNumber];
    username = loginCredential[“username”];
    password = loginCredential[“password”];
    result = request601.POST(‘/ShoppingCart/Login’,
    ( NVPair(‘login’, username),
    NVPair(‘passwd’, password), ),
    ( NVPair(‘Content-Type’, ‘application/x-www-form-urlencoded’), ))
    self.token_sessionId = \

    The Console show me the following:-
    Error running worker process (SyntaxError: (‘invalid syntax’, (‘\\current\\examples\\shoppingcart_1.py’, 269, 10, ‘ loginCredential = loginCredentials[grinder.threadNumber];’))
    (no code object) at line 0)

    Please let me know where am i going wrong
    Thanks.

    Reply
  10. vivin says:
    February 11, 2010 at 8:53 am

    @vishal sharma
    Hey Vishal,

    It looks like a yyntax error of some sort, but I can’t see it from your code. Maybe you have some sort of syntax error earlier on?

    Reply
  11. Sudhu says:
    April 4, 2010 at 11:31 am

    Hi Vivin,

    Thanks for your article. Just i want me to get clarified on settings in grinder.properties.Suppose I want to load test with two grinder scripts a.py and b.py simultaneously. So i assign two agents each for a script. I will be invoking these two agents from same console and each agent will have separate grinder.properties. So each will have separate grinder.properties. So could you please confirm if above is right way and how can i implement virtual users say count of 20 ?. And i also wan t know what will be difference in setting grinder.threads=1 and grinder.threads=2 while executing the script.

    Reply
  12. Stephanie says:
    August 5, 2010 at 3:52 pm

    This is very useful article for me as a first time Grinder load testing user. Very meaningful explanation. Good work.

    Reply
  13. Borislav Andruschuk says:
    September 29, 2010 at 3:45 am

    The Grinder is great but it miss IDE for debugging scripts and build modular projects. We develop The GrinderStone for clear these issues and allow create scripts faster. The GrinderStone is an Eclipse plugin, you can find more information on our official project site:

    http://code.google.com/p/grinderstone/

    Reply
  14. mei says:
    October 29, 2010 at 2:07 pm

    @vivin

    Hi Vivin,

    Thank you for the article. I am a newbie for grinder and was trying to get more understanding. I was able to record, but when I tried to play back I am keep getting error message below. Any suggestio will be appreciated!
    ====================================================

    C:\grinder-3.4\bin>call C:\grinder-3.4\bin\setGrinderEnv.cmd

    C:\grinder-3.4\bin>set GRINDERPATH=C:\grinder-3.4\

    C:\grinder-3.4\bin>set GRINDERPROPERTIES=C:\grinder-3.4\\properties\grinder.prop
    erties

    C:\grinder-3.4\bin>set CLASSPATH=C:\grinder-3.4\\lib\grinder.jar;

    C:\grinder-3.4\bin>set JAVA_HOME=C:\Java\jdk1.6.0_22

    C:\grinder-3.4\bin>PATH=C:\Java\jdk1.6.0_22\bin;C:\WINDOWS\system32;C:\WINDOWS;C
    :\WINDOWS\System32\Wbem

    C:\grinder-3.4\bin>echo C:\grinder-3.4\\lib\grinder.jar;
    C:\grinder-3.4\\lib\grinder.jar;

    C:\grinder-3.4\bin>java -cp C:\grinder-3.4\\lib\grinder.jar; net.grinder.Grinder
    C:\grinder-3.4\\properties\grinder.properties
    10/29/10 12:59:50 PM (agent): The Grinder 3.4
    10/29/10 12:59:50 PM (agent): connected to console at localhost/127.0.0.1:6372
    10/29/10 12:59:50 PM (agent): waiting for console signal
    10/29/10 1:00:13 PM (agent): received a start message
    10/29/10 1:00:13 PM (agent): Worker process command line: java ‘-javaagent:C:\gr
    inder-3.4\lib\grinder-agent.jar’ -classpath ‘C:\grinder-3.4\\lib\grinder.jar;’ n
    et.grinder.engine.process.WorkerProcessEntryPoint
    10/29/10 1:00:13 PM (agent): worker dauntless -0 started
    *sys-package-mgr*: processing new jar, ‘C:\grinder-3.4\lib\grinder.jar’
    *sys-package-mgr*: processing new jar, ‘C:\grinder-3.4\lib\grinder-agent.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\resources.ja
    r’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\rt.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\jsse.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\jce.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\charsets.jar
    ‘
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\ext\dnsns.ja
    r’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\ext\localeda
    ta.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\ext\sunjce_p
    rovider.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\ext\sunmscap
    i.jar’
    *sys-package-mgr*: processing new jar, ‘C:\Java\jdk1.6.0_22\jre\lib\ext\sunpkcs1
    1.jar’
    10/29/10 1:00:17 PM (process dauntless -0): Error running worker process (: No module named new
    import new
    File “C:\grinder-3.4\properties\grinder.py “, line 1166, in instrumentMe
    thod
    instrumentMethod(Test(100, ‘Page 1’), ‘page1’)
    File “C:\grinder-3.4\properties\grinder.py “, line 1172, in )
    10/29/10 1:00:18 PM (agent): finished, waiting for console signal

    ====================================================

    Reply
  15. Ashok says:
    February 8, 2011 at 9:37 pm

    Excellent start-up for the new comers who interested in evaluating the grinder.

    Thanks a lot Vivin

    Reply
  16. Ashok says:
    February 8, 2011 at 9:43 pm

    Vivin can you help me out in using the external file as my parameter file, Is it possible with Grinder Tool.

    In brief : there is a excel sheet or text document which have the username,password.

    ashok,ashok
    suri,suri
    rama,rama

    i want to call this file and pick the value sequentially for each iteration.

    Iteration 1 : ashok/ashok
    Iteration 2 : suri/suri etc

    Reply
  17. Pingback: Tweets that mention Performance testing using The Grinder | Rough Book -- Topsy.com
  18. Julio Leiva says:
    March 2, 2011 at 3:36 pm

    Excelent article
    I’m a new using grinder, I was able to run some test without any problem
    have a question though
    How can I grab the response back from the web page for example
    I type in a wrong password, I will get a response back from the server ‘invalid user id or password’.
    it is possible to do grab it in grinder?

    Thanks a lot in advance

    Reply
  19. devis says:
    August 19, 2011 at 5:37 am

    great article, thank you!

    could you just remove the java applet ? it’s killing my browser 😉

    Reply
  20. Mohit says:
    September 1, 2011 at 12:34 pm

    Hi, can you share the web application of infusion test ??

    Reply
  21. itisha says:
    February 29, 2012 at 7:58 am

    good article for new to Grinder

    Reply
  22. Renuka says:
    March 19, 2012 at 3:36 am

    We are trying to use this grinder tool in our Project I’m facing some problems while running the scripts in the tool.can you please help me on this.

    Reply
  23. amy says:
    October 30, 2012 at 1:57 pm

    I am a new for Grinder, but your article is a big help. I want to say thank you so much for sharing your experience with us.

    Thank you again!!!

    Reply
  24. Srinivas says:
    December 5, 2012 at 7:48 am

    Very useful article for anyone to start. I have some queries regarding
    1. How do we verify that user is logged in?
    2. I am encountering the error message “no module named os” in grinder analyzer.

    Can you provide some input.

    Thanks
    Srinivas

    Reply
  25. Bhavya says:
    January 29, 2013 at 12:40 am

    It really worked for me

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Archives

  • February 2023
  • April 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • May 2019
  • March 2019
  • February 2019
  • January 2019
  • December 2018
  • November 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • January 2018
  • December 2017
  • November 2017
  • October 2017
  • June 2017
  • March 2017
  • November 2016
  • August 2016
  • July 2016
  • June 2016
  • February 2016
  • August 2015
  • July 2014
  • June 2014
  • March 2014
  • December 2013
  • November 2013
  • September 2013
  • July 2013
  • June 2013
  • March 2013
  • February 2013
  • January 2013
  • October 2012
  • July 2012
  • June 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • July 2011
  • June 2011
  • May 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
  • March 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • August 2008
  • March 2008
  • February 2008
  • November 2007
  • July 2007
  • June 2007
  • May 2007
  • March 2007
  • December 2006
  • October 2006
  • September 2006
  • August 2006
  • June 2006
  • April 2006
  • March 2006
  • January 2006
  • December 2005
  • November 2005
  • October 2005
  • September 2005
  • August 2005
  • July 2005
  • June 2005
  • May 2005
  • April 2005
  • February 2005
  • October 2004
  • September 2004
  • August 2004
  • July 2004
  • June 2004
  • May 2004
  • April 2004
  • March 2004
  • February 2004
  • January 2004
  • December 2003
  • November 2003
  • October 2003
  • September 2003
  • July 2003
  • June 2003
  • May 2003
  • March 2003
  • February 2003
  • January 2003
  • December 2002
  • November 2002
  • October 2002
  • September 2002
  • August 2002
  • July 2002
  • June 2002
  • May 2002
  • April 2002
  • February 2002
  • September 2001
  • August 2001
  • April 2001
  • March 2001
  • February 2001
  • January 2001
  • December 2000
  • November 2000
  • October 2000
  • August 2000
  • July 2000
  • June 2000
  • May 2000
  • March 2000
  • January 2000
  • December 1999
  • November 1999
  • October 1999
  • September 1999
©2023 Rough Book | Built using WordPress and Responsive Blogily theme by Superb
All original content on these pages is fingerprinted and certified by Digiprove