Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Load Testing a Ruby on Rails Application

DZone's Guide to

Load Testing a Ruby on Rails Application

Load testing a typical Rails application is is a start to things like keeping track of sessions, CSRF token handling, and submitting forms.

· Performance Zone ·
Free Resource

xMatters delivers integration-driven collaboration that relays data between systems, while engaging the right people to proactively resolve issues. Read the Monitoring in a Connected Enterprise whitepaper and learn about 3 tools for resolving incidents quickly.

In this post we highlight some of the common areas which need to be catered to when load testing a typical Rails application. This is a start to things like keeping track of sessions, CSRF token handling, and submitting forms.

For more information about the DSL refer to our github repository.

Sessions

Most applications need to keep track of a certain state of a particular user. This could be the contents of a shopping basket or the user ID of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request. Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.

Typical rails apps keeps track of sessions via cookies. You can keep track of cookies on a per-user basis with the sessions ruby-jmeter method.

Cross Site Request Forgery (csrf)

First, as is required by the W3C, rails apps will use GET and POST appropriately. Secondly, a security token in non-GET requests will protect the app from CSRF. A typical rails app will embed a csrf-token into response bodies of requests. Look for code of a similar pattern in the head or body of the response:

<meta content="authenticity_token" name="csrf-param" /> <meta content="m4vGNR2Oj2ZhYDg8AgTq1+0EgZi3NQKI89rxxsGUIU4=" name="csrf-token" />


You can keep track of these tokens on a per-user basis with the extract ruby-jmeter method:

 extract name: 'authenticity_token', regex: 'meta content="(.+?)" name="csrf-token"' 

This will automatically extract the CSRF token whenever it occurs and keep track of it via a JMeter variable ${authenticity_token} . 

Rails Forms

The Rails framework includes form helpers (form_tag)   which will create a form tag which, when submitted, will POST to the current page. For instance, assuming the current page is /home/index, the generated HTML will look like this (some line breaks added for readability):

<form accept-charset="UTF-8" action="/home/index" method="post">

<div >

<input type="text" name="<span class=" />utf8" type="hidden" value="✓" />

<input name="authenticity_token" type="hidden"

value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />

<input id="q" name="q" type="text" />

</div>

Form contents

</form>


You’ll notice that the HTML contains something extra: a div element with two hidden input elements inside. This div is important, because the form cannot be successfully submitted without it. The first input element with name UTF-8 enforces browsers to properly respect your form’s character encoding and is generated for all forms whether their actions are “GET” or “POST”.

The second input element with name authenticity_token is a security feature of Rails called cross-site request forgery protection, and form helpers generate it for every non-GET form (provided that this security feature is enabled).

You can submit forms with the submit ruby-jmeter method passing in parameters to the form with fill_in:

submit '/store/register/${cart_id}', { 

  fill_in: {

    'utf8'                            => '✓',

    'authenticity_token'              => '${authenticity_token}',

    'q'                               => 'Some Dynamic Query'

  }

}


This will submit the form via an HTTP POST with the correct parameters for character encoding, csrf-token and any other form field parameters.

A Complete Example

Let’s look at an end-to-end example for a trivial rails app. In this example, we create a test plan which has HTTP request defaultspointing to the domain my.site.com

We are using the cache method to keep track of a per user simulated browser cache with clear_each_iteration set to true to simulate an empty browser cache at the start of each iteration.

We are using the cookies method to keep track of per user cookie sessions handled by rails.

We have a threads group with 50 users and a ramp up time of 60 seconds and a test duration of 120 seconds.

We are using the random_timer method to space out user requests on a varying 1 – 3 second interval (specified in milliseconds).

We are using the extract method to automatically parse out any csrf-token found in the response bodies to a request. Notice we don't have to scope this to individual requests; we keep it at the top of the thread group so it applies to all request response bodies.

Our first transaction is a simple visit via a HTTP GET to the home page. The transaction method provides a simple way to label a group of samples. We are also using the assert method to check that the home page response body contains some expected text.

Our second transaction uses submit to HTTP POST a form to our login page, with fill_in parameters. The parameters submitted are the character encoding, ${authenticity_token} which was automatically populated by the previous extract method, a user name and password as well as the commit parameter associated with this form.

Following is the completed end to end example described above.

Running the Test

require 'ruby-jmeter'

test do

defaults domain: 'my.site.com'

cache clear_each_iteration: true

cookies

threads 50, {ramp_time: 60, duration: 120, continue_forever: true} do

random_timer 1000, 3000

extract name: 'authenticity_token',

            regex: 'meta content="(.+?)" name="csrf-token"'

transaction '01_my_site_visit_home_page' do

      visit '/' do

        assert contains: 'Welcome to my site'

      end

    end

transaction '02_my_site_login' do

      submit '/login', {

        fill_in: {

          'utf8'                                            => '%E2%9C%93',

          'authenticity_token'                => '${authenticity_token}',

          'user[name]'                              => 'Timmy',

          'user[password]'                       => 'Tables'

          'commit'                                     => 'Sign In',

        }

      }

    end

end

end


If you are using Flood IO, you can run tests from the ruby-jmeter DSL automatically using the.flood method. This method requires your Flood IO API key. We recommend you set an environment variable similar to the following:

test do 

  threads 50 do

    ...

  end

end.flood ENV['API_TOKEN'], { 

  region: 'us-west-1',

  name: 'Demo'

}

Discovering, responding to, and resolving incidents is a complex endeavor. Read this narrative to learn how you can do it quickly and effectively by connecting AppDynamics, Moogsoft and xMatters to create a monitoring toolchain.

Topics:
ruby ,ruby on rails ,load testing ,performance testing ,performance

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}