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

Create a Checkout Load Test Scenario with Gatling

DZone's Guide to

Create a Checkout Load Test Scenario with Gatling

Gatling is an open source load testing Scala application. Unlike Apache JMeter, Gatling doesn't have a GUI interface. If you have an e-commerce site, this is for you.

· Performance Zone ·
Free Resource

Maintain Application Performance with real-time monitoring and instrumentation for any application. Learn More!

Gatling is an open source load testing Scala application. Unlike Apache JMeter™, Gatling doesn't have a GUI interface. So, when you want to simulate a given scenario, you do it by writing a Scala script. If you want to learn more about Gatling and the differences between Gatling and JMeter, click here.

In this blog post we will simulate a typical checkout scenario using Gatling. If you have an e-commerce site, this blog post is for you.

Let's look at the flow we want to automate. We are going to an example e-commerce website.

Imagine the user enters your website and types in "iphone" in the search bar:

The corresponding script for the iphone search:

val cart = scenario("Cart")
// HOME PAGE
.exec(http("get home page")
 .get("/")
 .headers(headers_0))

// search
.exec(http("search")
 .get("/index.php?route=product/search&search=Iphone")
 .headers(headers_0)
  .check(status.is(200)))

.exec(http("selectIphone")
 .get("http://" + uri2 + "/index.php?route=product/product&product_id=40&search=Iphone")
 .headers(headers_6)
 .check(status.is(200))
 .resources(http("iphonePage")
 .get("http://" + uri2 + "/index.php?route=product/product/review&product_id=40")
 .headers(headers_7))
 .check(status.is(200)))


The search returns the phones that are available on the website. The user picks up the one she or he likes, adds it to the cart, and goes to checkout:

The script:

// add to cart
.exec(http("add to cart")
.post("http://opencart.abstracta.us/index.php?route=checkout/cart/add")
.headers(headers_10)
.formParam("quantity", "1")
.formParam("product_id", "40")
.resources(http("getCart")
.get("http://" + uri2 + "/index.php?route=common/cart/info")
.headers(headers_7))
 .check(status.is(200))

// checkout
.headers(headers_2)
.resources(http("checkoutPage")
.get("/index.php?route=checkout/checkout")
.headers(headers_0),
         http("showLogin")
.get("/index.php?route=checkout/login")
.headers(headers_14))
 .check(status.is(200)))


.exec(http("Login")
.post("/index.php?route=checkout/login/save")
.headers(headers_16)
.formParam("account", "register")
.formParam("account", "guest")
.formParam("", "Continue")
.formParam("email", "JohnLennon@abstracta.com.uy")
.formParam("password", "1234")
.formParam("", "Login")
.resources(http("checkout")
.get("/index.php?route=checkout/checkout")
.check(status.is(200))
.headers(headers_0),
         http("paymentAddress")
.get("/index.php?route=checkout/payment_address")
.check(status.is(200))
.headers(headers_14),
         http("country")
.get("/index.php?route=checkout/checkout/country&country_id=222")
.headers(headers_19))
.check(status.is(200)))


Once there, the user logs in, complete the billing information and places the order.

Image title


.exec(http("billingInfo")
.post("/index.php?route=checkout/payment_address/save")
.headers(headers_16)
.formParam("payment_address", "existing")
.formParam("address_id", "155")
.formParam("firstname", "")
.formParam("lastname", "")
.formParam("company", "")
.formParam("address_1", "")
.formParam("address_2", "")
.formParam("city", "")
.formParam("postcode", "")
.formParam("country_id", "222")
.formParam("zone_id", "")
.resources(http("shippingAddress")
.get("/index.php?route=checkout/shipping_address")
.check(status.is(200))
.headers(headers_14),
         http("request_22")
.get("/index.php?route=checkout/checkout/country&country_id=225")
.check(status.is(200))
.headers(headers_19),
         http("request_23")
.get("/index.php?route=checkout/payment_address")
.check(status.is(200))
.headers(headers_14),
         http("request_24")
.get("/index.php?route=checkout/checkout/country&country_id=225")
.check(status.is(200))
.headers(headers_2),
         http("request_26")
.post("/index.php?route=checkout/shipping_address/save")
.headers(headers_16)
.formParam("shipping_address", "existing")
.formParam("address_id", "155")
.formParam("firstname", "")
.formParam("lastname", "")
.formParam("company", "")
.formParam("address_1", "")
.formParam("address_2", "")
.formParam("city", "")
.formParam("postcode", "90200")
.formParam("country_id", "225")
.formParam("zone_id", "3696"),
         http("request_27")
.get("/index.php?route=checkout/shipping_method")
.check(status.is(200))
.headers(headers_14),
         http("request_28")
.get("/index.php?route=checkout/shipping_address")
.check(status.is(200))
.headers(headers_14),
         http("request_29")
.get("/index.php?route=checkout/checkout/country&country_id=225")
.check(status.is(200))
.headers(headers_19),
         http("request_30")
.get("/index.php?route=checkout/payment_address")
.check(status.is(200))
.headers(headers_14),
         http("request_31")
.get("/index.php?route=checkout/checkout/country&country_id=225")
.headers(headers_19))
.check(status.is(200)))

.exec(http("Shipping")
.post("/index.php?route=checkout/shipping_method/save")
.headers(headers_16)
.formParam("shipping_method", "flat.flat")
.formParam("comment", "")
.resources(http("request_33")
.get("/index.php?route=checkout/payment_method")
.headers(headers_14))
.check(status.is(200)))


.exec(http("Payment")
.post("/index.php?route=checkout/payment_method/save")
.headers(headers_16)
.formParam("payment_method", "cod")
.formParam("comment", "")
.formParam("agree", "1")
.resources(http("request_38")
.get("/index.php?route=checkout/confirm")
.headers(headers_14))
.check(status.is(200)))

.exec(http("confirmPayment")
.get("/index.php?route=extension/payment/cod/confirm&_=1506910033976")
.headers(headers_39)
.resources(http("request_40")
.get("http://" + uri2 + "/index.php?route=checkout/success"))
.check(status.is(200)))

.exec(http("request_42")
.get("http://" + uri2 + "/index.php?route=common/home")
.headers(headers_6)
.check(status.is(200)))


This is the complete script. It consists of all the parts we went through as well as the scenario simulation. You can copy and paste it, and start trying it out.

class Cart extends Simulation {
private val baseUrl = "https://opencart.abstracta.us"

val httpProtocol = http
 .baseURL(baseUrl)
 .inferHtmlResources()
 .acceptHeader("*/*")
 .acceptEncodingHeader("gzip, deflate")
 .acceptLanguageHeader("en-US,en;q=0.5")
 .userAgentHeader("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0")

val headers_0 = Map(
 "Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
 "DNT" -> "1",
 "Upgrade-Insecure-Requests" -> "1")

val headers_1 = Map("DNT" -> "1")

val headers_2 = Map(
 "Accept-Encoding" -> "gzip, deflate",
 "Pragma" -> "no-cache")

val headers_6 = Map(
 "Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
 "Accept-Encoding" -> "gzip, deflate",
 "DNT" -> "1",
 "Upgrade-Insecure-Requests" -> "1")

val headers_7 = Map(
 "Accept" -> "text/html, */*; q=0.01",
 "Accept-Encoding" -> "gzip, deflate",
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")

val headers_8 = Map(
 "Accept-Encoding" -> "gzip, deflate",
 "DNT" -> "1")

val headers_10 = Map(
 "Accept" -> "application/json, text/javascript, */*; q=0.01",
 "Accept-Encoding" -> "gzip, deflate",
 "Content-Type" -> "application/x-www-form-urlencoded; charset=UTF-8",
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")

val headers_14 = Map(
 "Accept" -> "text/html, */*; q=0.01",
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")

val headers_16 = Map(
 "Accept" -> "application/json, text/javascript, */*; q=0.01",
 "Content-Type" -> "application/x-www-form-urlencoded; charset=UTF-8",
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")

val headers_19 = Map(
 "Accept" -> "application/json, text/javascript, */*; q=0.01",
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")

val headers_39 = Map(
 "DNT" -> "1",
 "X-Requested-With" -> "XMLHttpRequest")


   val uri2 = "opencart.abstracta.us"


val cart = scenario("Cart")
 // HOME PAGE
 .exec(http("get home page")
  .get("/")
  .headers(headers_0))

 // search
 .exec(http("search")
  .get("/index.php?route=product/search&search=Iphone")
  .headers(headers_0)
   .check(status.is(200)))

 .exec(http("selectIphone")
  .get("http://" + uri2 + "/index.php?route=product/product&product_id=40&search=Iphone")
  .headers(headers_6)
  .check(status.is(200))
  .resources(http("iphonePage")
  .get("http://" + uri2 + "/index.php?route=product/product/review&product_id=40")
  .headers(headers_7))
  .check(status.is(200)))

 // add to cart
 .exec(http("add to cart")
  .post("http://opencart.abstracta.us/index.php?route=checkout/cart/add")
  .headers(headers_10)
  .formParam("quantity", "1")
  .formParam("product_id", "40")
  .resources(http("getCart")
  .get("http://" + uri2 + "/index.php?route=common/cart/info")
  .headers(headers_7))
   .check(status.is(200))

 // checkout
  .headers(headers_2)
  .resources(http("checkoutPage")
  .get("/index.php?route=checkout/checkout")
  .headers(headers_0),
           http("showLogin")
  .get("/index.php?route=checkout/login")
  .headers(headers_14))
   .check(status.is(200)))


 .exec(http("Login")
  .post("/index.php?route=checkout/login/save")
  .headers(headers_16)
  .formParam("account", "register")
  .formParam("account", "guest")
  .formParam("", "Continue")
  .formParam("email", "JohnLennon@abstracta.com.uy")
  .formParam("password", "1234")
  .formParam("", "Login")
  .resources(http("checkout")
  .get("/index.php?route=checkout/checkout")
  .check(status.is(200))
  .headers(headers_0),
           http("paymentAddress")
  .get("/index.php?route=checkout/payment_address")
  .check(status.is(200))
  .headers(headers_14),
           http("country")
  .get("/index.php?route=checkout/checkout/country&country_id=222")
  .headers(headers_19))
  .check(status.is(200)))

 .exec(http("billingInfo")
  .post("/index.php?route=checkout/payment_address/save")
  .headers(headers_16)
  .formParam("payment_address", "existing")
  .formParam("address_id", "155")
  .formParam("firstname", "")
  .formParam("lastname", "")
  .formParam("company", "")
  .formParam("address_1", "")
  .formParam("address_2", "")
  .formParam("city", "")
  .formParam("postcode", "")
  .formParam("country_id", "222")
  .formParam("zone_id", "")
  .resources(http("shippingAddress")
  .get("/index.php?route=checkout/shipping_address")
  .check(status.is(200))
  .headers(headers_14),
           http("request_22")
  .get("/index.php?route=checkout/checkout/country&country_id=225")
  .check(status.is(200))
  .headers(headers_19),
           http("request_23")
  .get("/index.php?route=checkout/payment_address")
  .check(status.is(200))
  .headers(headers_14),
           http("request_24")
  .get("/index.php?route=checkout/checkout/country&country_id=225")
  .check(status.is(200))
  .headers(headers_2),
           http("request_26")
  .post("/index.php?route=checkout/shipping_address/save")
  .headers(headers_16)
  .formParam("shipping_address", "existing")
  .formParam("address_id", "155")
  .formParam("firstname", "")
  .formParam("lastname", "")
  .formParam("company", "")
  .formParam("address_1", "")
  .formParam("address_2", "")
  .formParam("city", "")
  .formParam("postcode", "90200")
  .formParam("country_id", "225")
  .formParam("zone_id", "3696"),
           http("request_27")
  .get("/index.php?route=checkout/shipping_method")
  .check(status.is(200))
  .headers(headers_14),
           http("request_28")
  .get("/index.php?route=checkout/shipping_address")
  .check(status.is(200))
  .headers(headers_14),
           http("request_29")
  .get("/index.php?route=checkout/checkout/country&country_id=225")
  .check(status.is(200))
  .headers(headers_19),
           http("request_30")
  .get("/index.php?route=checkout/payment_address")
  .check(status.is(200))
  .headers(headers_14),
           http("request_31")
  .get("/index.php?route=checkout/checkout/country&country_id=225")
  .headers(headers_19))
  .check(status.is(200)))

 .exec(http("Shipping")
  .post("/index.php?route=checkout/shipping_method/save")
  .headers(headers_16)
  .formParam("shipping_method", "flat.flat")
  .formParam("comment", "")
  .resources(http("request_33")
  .get("/index.php?route=checkout/payment_method")
  .headers(headers_14))
  .check(status.is(200)))


 .exec(http("Payment")
  .post("/index.php?route=checkout/payment_method/save")
  .headers(headers_16)
  .formParam("payment_method", "cod")
  .formParam("comment", "")
  .formParam("agree", "1")
  .resources(http("request_38")
  .get("/index.php?route=checkout/confirm")
  .headers(headers_14))
  .check(status.is(200)))

 .exec(http("confirmPayment")
  .get("/index.php?route=extension/payment/cod/confirm&_=1506910033976")
  .headers(headers_39)
  .resources(http("request_40")
  .get("http://" + uri2 + "/index.php?route=checkout/success"))
  .check(status.is(200)))

 .exec(http("request_42")
  .get("http://" + uri2 + "/index.php?route=common/home")
  .headers(headers_6)
  .check(status.is(200)))


setUp(
 cart
  .inject(rampUsers(10) over (2 minutes)).protocols(httpProtocol))
 .assertions(
  global.requestsPerSec.gt(10),
  global.responseTime.percentile3.lt(300),
  global.failedRequests.percent.lt(1)
 )
}

Gatling Script Pointers

Gatling automatically follow redirects in case of 301, 302, 303, or 307 response status codes. If you don't want that to happen, you can change that by adding .disableFollowRedirect to the httpprotocolval part of the code.

Every response status code is easily checked with this line of code .check(status.is(200). If the response is 200, return true; otherwise, return false and the assertion will fail.

Finally, in order to run a performance test, you need to decide how many users you are running, how they will behave (will they hit the page at the same time, will they load in a linear way, etc. ), and for how long the test will run. Gatling allows us to accomplish all of this in a very flexible way.

We can setup different scenarios right away, for example:

This single line of code will add 10 users all at the same time:

setUp(scenario1.inject(atOnceUsers( 10 )) .protocols(httpProtocol))

This other scenario, (used in the script) will ramp-up 10 users over a period of 2 minutes and also use global assertions:

setUp(cart.inject(rampUsers(10) over (2 minutes )).protocols(httpProtocol))
    .assertions(
    global.requestsPerSec.gt(10),
    global.responseTime.percentile3.lt(300),
    global.failedRequests.percent.lt(1)
    )
}


In this case, these assertions will check that the requests per seconds was greater than 10, the response time percentile 95 was less than 300 ms and that the percentage of failed request must be below 1%. If you want to know more about simulating differents scenarios in Gatling, please read this.

Gatling Test Results

Once the script has run, Gatling will generate a beautiful html report like this one:

You can see that the global assertion for the response time failed.

You also can also see statistics about the response time of all the requests.

and the response time percentiles:

That's it! You now know how to run a checkout scenario load test in Gatling.

Automate your Gatling tests through Taurus. You can also easily scale your Gatling load tests, run them in the cloud and analyze the results on BlazeMeter.

Collect, analyze, and visualize performance data from mobile to mainframe with AutoPilot APM. Learn More!

Topics:
performance testing ,website crashes ,open source load testing tools ,gatling ,e-commerce load testing ,jmeter ,test script ,black friday ,load testing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}