Anatomy of a Grinder test-script

by vivin

This is my second post regarding Grinder. In this post I’ll go over the anatomy of a recorder Grinder test-script. If you haven’t read my previous post, please take a look at it. Otherwise this post won’t make much sense!

High-level structure of a Grinder script

The high-level structure of a recorded Grinder-test-script looks like this:

The first two import statements import the Test and grinder objects, which give you access to Grinder’s testing framework, and the grinder environment. The next two import statements import some utility classes which perform HTTP Requests. The NVPair class lets you organize parameters and values for POST requests into name-value pairs.

Header definitions

When the recorder runs, it records every request that the browser makes. After that, it goes through all the requests and generates a set of unique headers. These headers are then used to create the request objects (which we’ll look at later). The generated code looks like this:

The first statement creates an HTTPPluginConnection object, which lets you control the behavior of the connection. The next statement creates an instance of an HTTPUtilities object which provides access to various utility methods (you’ll see examples of this later). The next two commented lines let you know that you can proxy these requests through a proxy server if you wish. The two comments after that let you know that these definitions are evaluated once for each process. Essentially, all data defined at the top of a script, outside the class, are shared between threads belonging to a single worker process.

In the next few lines, the script actually defines the headers. First, it sets up the default headers that are used by each request (things like the User-Agent, Charset, and Encoding). After that, it goes through and defines the headers. As you can see, these headers are basically tuples of NVPair objects. One NVPair defines the Accept parameter, and the other defines the Referer. Those with an eye for detail will notice that there seems to be an extra comma at the end of the tuple definition. This isn’t a problem in Python because the parser will treat the last element as an empty element.

URL definitions

In the next section of the script, you’ll find URL definitions, which look like this:

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

In this example, there is only one URL. But let’s say that the app you’re testing hits a few different URL’s during the scenario that you’re recording. Then the script will have a definition for each unique URL that the recorder encountered. The definitions are of the form urlN = “protocol://recorded.url.here”. The combination of a URL and Header is what defines a request (more on that in the next section).

Request and test definitions

In the next section of the script, we define the actual requests that we’re going to use for our tests:

The comments give a terse explanation of what’s happening, but allow me to elaborate. First, you create an instance of an HTTPRequest object using the previously defined URL’s and Headers. An important thing to note here is that there is a naming scheme for the requests, which is of the form request[Page Number][Request Number]. So, for example, request105 means the fifth request in the first recorded page, and request1101 means the first request in the eleventh recorded page (yes, there is an inherent ambiguity, because 1101 could also mean the one hundred and first request in the first recorded page. But if you have a hundred and one unique requests per page, then you have a problem).

The next statement is the most important one. The way Grinder records test statistics is by proxying whatever you want to test, through the Test object. What this means is that the Test object is a wrapper around the object you want to test. You can still treat the wrapped object like the original object, but under the hood, access to the methods of that object are proxied through the Test object. This way, Grinder can record statistics. The picture might help understand what’s going on:

On the top you can see how a regular request behaves when accessing the GET method. It’s a straight call. The bottom picture shows you what happens when you wrap your request object with a Test object. Now, the call to GET isn’t made on the actual request object. It’s proxied through the Test object, which has an attribute called __target__ (which is the actual request object). Once it gets to the actual request object, the actual GET method is called. The reason Grinder does this is so that it can collect statistics on actions performed by an object.

Method definitions

After the request objects have been defined, we come to the actual TestRunner class. This is where you define methods that correspond to each recorded page:

In the script, you have a method for each recorded page, which are called page1, page2, and so forth. Within each page, there are one or more GET and/or POST requests made via the request objects, which simulate the transactions made by a single page. Let’s take a look at the page1 method. In this method you can see that it makes three requests. The first two requests are simple requests without any parameters. The last request, however, is a GET request with a querystring. The script uses the httpUtilities variable (defined earlier, at the beginning of the script) to get the value of the msg parameter from the URI, and constructs the querystring for the request.

The page2 method provides an example of a POST request. The values to the POST are supplied in name-value pairs using the NVPair object. Also notice the calls to grinder.sleep(). These calls simulate “think time”.

In both methods, you can see that the return value from the first request is assigned to a variable called result. The method then returns this variable (which contains statistics from Grinder).

Utility method, and calls to the utility method

I’m going to go out of order here and talk about the instrumentMethod utility method before I talk about the __call__ method. The instrumentMethod and the calls to that method look like this:

The instrumentMethod method is another way to proxy calls through the Test object, except, instead of proxying an object, it proxies a method. To accomplish this, instrumentMethod performs some metaprogramming. Metaprogramming lets you modify a class’s properties and methods during runtime. You can add, remove, and modify existing methods and properties. The instrumentMethod method has three arguments. The first argument is a test object, the second is the method name (as a string), and the third argument is a default argument that is set to TestRunner. First, instrumentMethod gets a reference to a TestRunner class method identified by method_name (assigned to the variable unadorned). Then it creates a new method (using new.instancemethod) which has the test object wrapped around the original method. instrumentMethod then assigns this new method back to the TestRunner class, effectively proxying all calls to the original method through the Test object.

__call__ method

In Python any method that implements the __call__ is _callable_, which basically means that this function is called when you call the class instance as a function. In the context of the this script, it just means that this is where all the testing starts off. In the __call__ method you can see that there are calls to all the recorded page methods (along with think time), and this essentially runs the recorded test.

Conclusion

Now you should have a general idea of what a recorded Grinder test-script looks like. In my next tutorial, I’ll go over writing performance tests in Grinder using a testing framework.

15 Responses to “Anatomy of a Grinder test-script”

Now this is something I can really appreciate…I’ve been looking for info on how to handle the security certificate issue (I’m not a developer) and am looking at using Grinder with Amazon EC2 to do load testing. Thanks for your posts!

[…] Performance Tests in Grinder using a FrameworkThe new vivin.netTen yearsAnatomy of a Grinder test-scriptPerformance testing using The GrinderThe Problem with Shaadi.comjQuery tip: Changing the TYPE […]

The Grinder is the best tool for performance testing. I’ve working on GrinderStone – IDE for Grinder scripts which allows debug scripts using Eclipse and provides some interesting features for development like modularity and pretty useful logging in debug mode. That project you can download from official project site:

we also have Eclipse Update site for simple plugin installation into Eclipse platform. All details you can obtain on our site and support group. GrinderStone gives you more power to develope Grinder scripts.

Thanks A lot for you great effort.
I want to know how do i log the request and response data and some customize (like first name last name user id password )data ?
what do need to import in
[import statements] section
[header definitions]section
and in other section also