Case Study: Mechanize Performance/Load Testing User Login with CSRF Token Protection

How to use Mechanize for a website’s performance and load testing

Preparation

gem install mechanize
require 'mechanize'
@browser = ::Mechanize.new
@browser.get("https://whenwise.agileway.net")
@browser.get("https://whenwise.agileway.net/sign-in")
login_form = @browser.page.forms.first
login_form.fields_with(:name => "session[email]").first.value = "driving@biz.com"
login_form.field_with(:name => "session[password]").value = "test01"
@browser.submit(login_form, login_form.button_with(:id => "sign-in-btn"))
# verify the specific text only shown to logged in user
raise "Not logged in" unless @browser.page.body.include?("Current Plan")
File.open("/tmp/a.html", "w").write(@browser.page.body)
# then open /tmp/a.html in Chrome
start_time = Time.now
# operation here ..
duration = Time.now - start_time
puts "took #{duration} seconds"
def log_time(msg, &block)
start_time = Time.now
begin
yield
ensure
end_time = Time.now - start_time
puts("#{msg} | #{end_time}")
end
end
log_time("Visit home page") { 
@browser.get("https://whenwise.agileway.net")
}

Performance Testing

require 'mechanize'

site_url = "https://whenwise.agileway.net"
@browser = ::Mechanize.new

log_time("Visit home page") { @browser.get(site_url) }
log_time("Visit login page") { @browser.get(site_url + "/sign-in") }

login_form = @browser.page.forms.first

log_time("enter data") {
login_form.field_with(name:"session[email]").value = "driving@biz.com"
login_form.field_with(:name=> "session[password]").value = "test01"
}

log_time("Login") {
@browser.submit(login_form, login_form.button_with(:id=> "sign-in-btn"))
}
|Visit home page  |1.088529|
|Visit login page |0.208664|
|enter data |0.000036|
|Login |1.043211|

The timing of “enter data” in the above example is unnecessary, as there is no interactions with the server. Those two steps simply prepare the form data for submission.

Simple Load Testing

ruby load_test.rb &; ruby load_test.rb &; ruby load_test.rb &

Threads, in software programming, are a way for a program to split itself into two or more simultaneously running tasks.

require 'mechanize'

@browser = ::Mechanize.new
virtual_user_count = 5 # may change concurrent users count here
threads = []

virtual_user_count.times do |idx|
threads[idx] = Thread.new do
Thread.current[:id] = idx + 1
@browser.get("https://whenwise.agileway.net")
@browser.get("https://whenwise.agileway.net/sign-in")
login_form = @browser.page.forms.first
login_form.field_with(:name => "session[email]").value = "driving@biz.com"
login_form.field_with(:name=> "session[password]").value = "test01"
@browser.submit(login_form, login_form.button_with(:id=> "sign-in-btn"))
raise "Not logged in " unless @browser.page.body.include?("Current Plan")
end
end

threads.each {|t| t.join; } # wait for all threads to complete
# ...
threads[idx] = Thread.new do
Thread.current[:id] = idx + 1
log_time("Visit home page") {
@browser.get("https://whenwise.agileway.net")
}
log_time("Visit login page") {
@browser.get("https://whenwise.agileway.net/sign-in")
}
login_form = @browser.page.forms.first
log_time("enter_data") {
login_form.field_with(:name => "session[email]").value = "driving@biz.com"
login_form.field_with(:name=> "session[password]").value = "test01"
}
log_time("Login") {
@browser.submit(login_form, login_form.button_with(:id=> "sign-in-btn"))
}
raise "Not logged in " unless @browser.page.body.include?("Current Plan")
end

threads.each {|t| t.join; }
1|Visit home page|1.069316
3|Visit home page|1.031282
2|Visit home page|1.088884
1|Visit login page|0.232194
1|enter_data|3.5e-05
3|Visit login page|0.247188
3|enter_data|3.1e-05
2|Visit login page|0.248053
2|enter_data|0.000107
1|Login|3.244234
3|Login|3.417211
2|Login|3.5701
The ‘Pay now’ button uses AJAX to load the receipt on the same page.
# ...
@browser.get("https://travel.agileway.net/flights/passenger/1")

passenger_form = @browser.page.forms.first
passenger_form.field_with(name: "passengerLastName").value = "Wise"
@browser.submit(passenger_form) # standard HTTP request, OK

payment_form = @browser.page.forms.first
File.write("/tmp/a.html", @browser.page.body)

@browser.submit(payment_form) # a AJAX operation
#...
502 => Net::HTTPBadGateway for https://travel.agileway.net/payment/confirm — 
unhandled response (Mechanize::ResponseCodeError)

I will write about a practical approach to load test AJAX operations in the next article.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store