Load Testing a Magento E-Commerce Shop

With Black Friday coming up you probably have high expectations for increased sales. Not to mention increased marketing spend. With these comes more customers so you’ve been asked to make sure your Magento Shop can handle the increased expected load.

However, Load Testing is no simple nut to crack.  Typically, specialized skills are needed to write a sophisticated load test at the protocol level.  Even then, writing a load test with current tools can take days if not weeks to complete. Given the amount of time this might take, it is likely impossible to get your site properly load testing with Black Friday around the corner.

Load Testing in Record Time with Flood Element

What you need is Flood Element to perform browser-level load testing: https://element.flood.io . Browser level load testing is about simulating real users doing real activity on your website instead of protocol level, which is more complicated to set up so the generated requests are close to reality. For example you can have a user search for an item on sale, add it to the cart, go to the checkout page and enter their payment information. In fact that’s what this tutorial is going to cover for a Magento shop!

Once you’ve written your script you can run it on Flood’s distributed load-testing platform to simulate hundreds or thousands of simultaneous users.

What’s Covered in This Tutorial

This tutorial will encompass everything that a non-load testing expert will need to get started testing a standard Magento e-commerce site including:

  1. Installing Flood Element and starting your project
  2.  How to test your script
  3.  Writing our load test
  4. Running the script on Flood

Here are some resources that will be useful during the course of this tutorial:

Flood signup

Flood element microsite

Step 1:  Installing Flood Element and setting up your project

Follow the install instructions to get Flood Element up and running, then initialize your new project with the `element init` command like so:

```bash
mkdir magento-load-test
cd magento-load-test
element init .
```

When asked for a url, use `http://magento2-demo.nexcess.net/` as this has been provided for the tutorial.

Once the project initialization process is complete you should see a directory structure like this:

```
magento-load-test/
node_modules
package.json
test.ts
tsconfig.json
yarn.lock
```

If you don’t have a node_modules folder yet then run `yarn` or `npm` to install the dependencies, whichever you prefer to use.

Our test script is the `test.ts` file. You can open it in your editor of choice but we recommend Visual Studio Code for its excellent out-of-the-box typescript support. You should see a script that has a single step and that step waits for the `http://magento2-demo.nexcess.net/` website to load before taking a screenshot.

Step 2: Run the script and get comfortable with the process

You run scripts with the `element run` command. There are a lot of ways you can run scripts. With or without a headless browsers, in watch mode, with the devtools open. You can read more about them with the help command:

```bash
element run --help
```

For now, run your script like below. This will watch for changes to the test.ts file, show the chrome window and open it with the dev tools.

```bash
element run test.ts --no-headless --devtools --watch
```

If it works, you should see Chromium open up and have it visit the `http://magento2-demo.nexcess.net/` website. Once the page has loaded you can find a screenshot in `magento-load-test/tmp/element-results/test`.

Step 3: Writing Our Load Test

This tutorial assumes you have a passing understanding of TypeScript, or at least modern JavaScript. If you need a primer on typescript, see the typescript docs.

Our test script already imports some functionality from `@flood/element`. These will be enough for our script today.

Flood Element tests are divided into steps. These steps can be divided however makes sense to you, however we recommend logical steps named after their purpose. Right now there is only one step:

```
+1342s info: Starting iteration 1
+1342s info:
+1342s info: ===> Step 'Test: Start'
+1345s info: ---> visit()
+1346s info: ---> Step 'Test: Start' finished
+1352s info: Iteration completed in 9266ms (walltime)
+1352s info: Test completed after 1 iterations
```

As the script builds up you’ll notice the `element run` output will show each step with a time next to it indicating how long it took and if it succeeded or not.

Before we start writing code, let’s plan our journey. The user will:

  1. Land on our landing page, in this case our homepage
  2. Search for an item in our shop
  3. Add the item to the shopping cart
  4. Visit the cart and make sure the item is there
  5. Checkout

Creating step one: user loads the landing page

This one is easy, all we need to do is rename the step,  and replace the url of the page we’re loading and remove all the comments.

```typescript
step('Test: User lands on the homepage', async browser => {
await browser.visit('https://magento.loadtest.io/')
await browser.takeScreenshot()
})
```

Creating step two: search for an item in our shop

For this step we’re going to use the search box in the top right of the homepage to search for shirts. We use the browser object’s `type` and `press` methods to simulate typing in _shirts_ and pressing return. After pressing return we are going to wait for the url to change to signify the page has changed.

```typescript
step('Test: User searches for an item from the homepage', async browser => {
// Find the search box element
const searchBox = By.css('#search')
await browser.wait(Until.elementIsVisible(searchBox))
// Search for our item
await browser.type(searchBox, 'shirt')
await browser.press(Key.RETURN)
await browser.wait(Until.urlContains('catalogsearch/result'))
})
```

If you save this step now, your browser should automatically re-run the test, visiting the homepage, typing into the search box and visiting the results page.

This is a good time to talk about assertions in browser-level load testing. There can be so many moving parts in a web page. Sometimes pages will appear to load, but are obviously broken. For example, if our search results loaded, but had no results when we expected to find 5 shirts it would mean something is broken. We can use asserts to verify the page state. Add the following code to your step:

```typescript
// Make sure we have results
const countSelector = By.css('.toolbar-amount')
await browser.wait(Until.elementIsVisible(countSelector))
const countElement = await browser.findElement(countSelector)
const countText = await countElement.text()
assert(countText === '5 items', 'Expected items are found')
```

Now our test will fail if we don’t have the expected amount of items. You can make your assertions as simple or as complex as you want. What matters is the first value is a boolean `true` for pass or `false` for fail.

Finally, we’re going to click the first result to take us to the new page.

```typescript
await browser.click(By.css('.product-image-photo:first-of-type'))
```

Creating step three: add the item to the shopping cart

We’re on the page to purchase our shirt and we want to add it to the cart. To make things complicated, we have to choose a colour and a size before we can add it to the cart. In our shop these don’t load immediately so it’s a little more complicated than waiting for the page to load and click them.

  1. First, we make our locators for all the elements and wait until they’re visible.
  2. Second, we click the options for black and large.
  3. Finally, we click the button to add it to the cart.
```typescript
step('Test: Add the item to the shopping cart', async browser => {
const black = By.css('.swatch-option.color:first-of-type')
const large = By.css('.swatch-option.text:nth-of-type(4)')
const addToCart = By.css('button.tocart')
await browser.wait(Until.elementIsVisible(black))
await browser.wait(Until.elementIsVisible(large))
await browser.wait(Until.elementIsVisible(addToCart))
await browser.click(black)
await browser.click(large)
await browser.click(addToCart)
})
```

For this step to be complete we should wait until we receive confirmation that the item was successfully added to the cart:

```typescript
const confirmationText = By.partialVisibleText('You added')
await browser.wait(Until.elementIsVisible(confirmationText))
await browser.takeScreenshot()
```

Creating step four: Visit the cart

This is another slightly complex navigation. In our shop the cart link shows a popover of the cart state instead of taking you directly to the cart. In the end it means we have to click it to show the popover, _and then_ click the view cart link inside the popover.

```typescript
step('Test: User visits the shopping cart', async browser => {
const showCart = By.css('.showcart')
await browser.click(showCart)
const viewCart = By.css('.viewcart')
await browser.wait(Until.elementIsVisible(viewCart))
await browser.click(viewCart)
await browser.wait(Until.urlContains('checkout/cart'))
})
```

Once there, like the search page we should assert the page has the correct information. This time we’re going to check the total price is correct.

```typescript
const total = By.css('.grand.totals .price')
await browser.wait(Until.elementIsVisible(total))
const totalElement = await browser.findElement(total)
const price = await totalElement.text()
await browser.takeScreenshot()
assert(price === '$45.00', 'Has the correct total in the cart')
```

Creating step five: Checkout

The checkout process is always a long step because there is so much user interaction necessary. Fields have to be filled, shipping options chosen. In our `magento.loadtest.io` demo site, you don’t have to enter payment info.

First, let’s go to the checkout page and fill out the form

```typescript
step('Test: User goes through the checkout process', async browser => {
await browser.click(By.visibleText('Proceed to Checkout'))
await browser.wait(Until.urlContains('checkout'))
// Wait for the form to load
await browser.wait(Until.elementIsVisible(By.visibleText('Shipping Address')))
// Prepare our inputs
const values = [
['#customer-email', 'you@example.com'],
['input[name=firstname]', 'Joe'],
['input[name=lastname]', 'LoadTester'],
['input[name="street[0]"]', '123 Fake St.'],
['input[name="city"]', 'Fakeville'],
['input[name="postcode"]', '90210'],
['input[name="telephone"]', '+1582582582'],
]
// Fill in all our details
for (let i = 0; i < values.length; i++) {
const element = By.css(values[i][0])
const value = values[i][1]
await browser.type(element, value)
}
// Choose the region
await browser.selectByText(By.css('select[name=region_id]'), 'California')
// Choose the shipping option
await browser.click(By.css('.table-checkout-shipping-method tbody .row:first-of-type .col-carrier'))
await browser.takeScreenshot()
})
```

Then, once shipping has been selected we can move onto payment. Our demo doesn’t have real payment, so this is the best we can do with the demo site.

```typescript
// Move onto payment
await browser.click(By.visibleText('Next'))
// Place the order!
const placeOrder = By.visibleText('Place Order')
await browser.wait(Until.elementIsVisible(placeOrder))
await browser.takeScreenshot()
await browser.click(placeOrder)
await browser.takeScreenshot()
```

Your site will have different payment requirements, but you can use the skills you learnt in this tutorial to write that part of the script. If you’d like to download our sample script, you can do so here: https://gist.github.com/kevindunneQA/1c9de7ce3a76142dfafc29820d69d1e6 .


Step 4: Running your load test on Flood

You’ve now got a script that can find and purchase an item on a magento shop. But how do we use it to test the performance of your shop under load? That’s where Flood’s distributed load testing platform comes in. Flood can take your Flood Element test script and run it distributed across the cloud to simulate all the different users.

  1. (If you haven’t already) sign up for flood.

2. Create a project: 

3. Click the “create flood” option in your new project

4. Create your test using the Flood Element Option:

5. Name your test:

6. Upload the .ts script you have created:

7. Set the number of browsers (users) as well as # of regions to get the total # of users (# of browsers * # of regions).  Also, don’t forget to set your test duration: 

8. Launch your test and wait for results to come in: 

8. Launch your test and wait for your results to come in: 

9. Once results are in, view your graph and jump in to any problematic areas they may highlight:

Conclusion

Load testing Magento shops can be hard, but it doesn’t have to be with the combination of a leading open source framework (Flood Element) and a browser based testing platform (Flood).  In just a couple of hours, you can be up and testing your site to make sure it is ready for your next promotion, whether that be Black Friday, Cyber Monday, or something totally different.  There’s no excuse for lost revenue or unhappy customers – so get out there and load test your site today!


Ready to get started?

Sign up or request a demo today.