One thing you can do to make your Magento deployments more testable

Well, it actually two things.

There are two main problems that I’ve run into when working with Magium and browser testing. The first one is timing, particularly with Ajax requests. Ajax is somewhat hard to test because something may or may not happen and you have to create a lot of WebDriver wait() statements to manage it (Do not use sleep(), use wait() (That advice is free)).

The second is selecting things. I generally standardize on Xpath, particularly in Magium itself. I do this because it gives me a tremendous amount of control over getting the exact element I need. CSS selectors are usually good enough, but they can’t always get the element that you need. Xpath usually does.

But it does so at the expense of verbocity. This is the Xpath that I use to extract the product price from a product page

(//form[@id="product_addtocart_form"]/descendant::span[contains(concat(" ",normalize-space(@class)," ")," regular-price ")]/span[contains(concat(" ",normalize-space(@class)," ")," price ")])[1]

That is because the HTML on the Magento 1.9 CE site for the price is this (significantly redacted):

<form action="http://magento19.loc/checkout/cart/add/uenc/aHR0cDovL21hZ2VudG8xOS5sb2Mvd29tZW4vdG9wcy1ibG91c2VzL2VsaXphYmV0aC1rbml0LXRvcC00OTMuaHRtbD9fX19TSUQ9VQ,,/product/421/form_key/iMf3OyZB7cnrAleQ/" method="post" id="product_addtocart_form">
   <input name="form_key" type="hidden" value="iMf3OyZB7cnrAleQ">
   <div class="no-display">
      <input type="hidden" name="product" value="421">
      <input type="hidden" name="related_product" id="related-products-field" value="">
   </div>
   <div class="product-shop">
   <div class="product-name">
      <span class="h1">Elizabeth Knit Top</span>
   </div>
   <div class="price-info">
      <div class="price-box">
         <span class="regular-price" id="product-price-421">
                     <span class="price">$210.00</span>                                    
                </span>
      </div>
   </div>
</form>

You don’t see it here but there is also the possibility that several other prices are displayed in that form as well.

To build the Xpath I first need to find the closest, predictable element. Ideally that would be the element I need, itself, but having something close, such as a parent container, is often enough.

You might be thinking that I have that in this snippet. If you look at the parent container for the price you see id="product-price-421". However, that is only predictable for this one product. I cannot use it on another page. So the closest, predictable, element is the form element with id="product_addtocart_form". That must be the base.

Now I need to get to the price. “Simple!” you might say. “//span[@class="price"]“. Unfortunately, no. As I mentioned earlier, the unredacted form content has several price elements and this Xpath snippet would match all of them. So we can’t use it. But on top of that, it is a @class element. That means that there is no need for it to be unique and it may end up having other classes included for the purpose of styling. That is what causes our verbosity. That is why I have to end up using span[contains(concat(" ",normalize-space(@class)," ")," regular-price ")]. It pads the normalized @class string and checks to see if it contains regular-price (note the padding in there). That’s what selecting by class name requires.

And on top of that, the price is not predictable either. For that reason I need to wrap the entire selector in parenthesis and state I want result [1].

One Rule to Rule Them All

Use IDs to designate elements that have meaning.

I kid you not. If you made your HTML just a little more verbose by assigning IDs to elements that had meaning browser testing becomes a lot easier. That meaning could either be an action, such as a submit button, or a display element, such as the price.

This also helps with testing different types of browsers. Sometimes developers like to duplicate their effort by making different menus or elements for different screen resolutions. This ends up making testing somewhat difficult. But that said, if you tag the appropriate elements with meaningful names it becomes much easier. In the initial example this could be done by simply changing the ID.

<span class="regular-price" id="product-price-desktop">
                     <span class="price">$210.00</span>                                    
                </span>

That, alone, has just made testing this significantly easier and would change the selector

(//form[@id="product_addtocart_form"]/descendant::span[contains(concat(" ",normalize-space(@class)," ")," regular-price ")]/span[contains(concat(" ",normalize-space(@class)," ")," price ")])[1]

to

//span[@id="product-price-desktop"]/span[@class="price"]

(yes, I know I’m presuming nobody adds a CSS element to the price). But that alone makes element selection, the key part of Selenium testing, significantly easier.

Some Other Rules

Don’t Use Generated IDs

They are almost impossible to predict. In the original example this looked like <span class="regular-price" id="product-price-421">. That is naming worthy of Admiral Ozzel. It’s using the product primary key as part of the ID. That makes it impossible to predict with certainty.

Wrap Text In HTML Tags and Make It Specific

Generally speaking, you don’t want to select based off of text. Text is notoriously difficult to predict in testing scenarios and the fact the HTML doesn’t care about your whitespace makes it even more difficult. But if you MUST select on the text make sure that the important part is wrapped with no whitespace.

You might think that our previous example for the price is good:

<span class="price">$210.00</span>

But it actually isn’t. It denotes two pieces of distinct information: currency and price. A better approach would be something like this:

<span class="price" id="product-price-desktop">
    <span class="currency">$</span>
        <span class="value">210.00</span>
</span>

That approach provides a much more succinct means of selecting by important elements.

Select on @title or @data-* elements

If you can’t guarantee no whitespace select on attributes that can.

For example, if you want to click the XL button on a configurable product form that looks like this:

<ul id="configurable_swatch_size" class="configurable-swatch-list clearfix">
   <li class="option-s" id="option80">
      <a href="javascript:void(0)" name="s" id="swatch80" class="swatch-link swatch-link-180" title="S" style="height: 23px; min-width: 23px;">
      <span class="swatch-label" style="height: 21px; min-width: 21px; line-height: 21px;">
      S                                 </span>
      <span class="x">X</span>
      </a>
   </li>
   <li class="option-m" id="option79">
      <a href="javascript:void(0)" name="m" id="swatch79" class="swatch-link swatch-link-180" title="M" style="height: 23px; min-width: 23px;">
      <span class="swatch-label" style="height: 21px; min-width: 21px; line-height: 21px;">
      M                                 </span>
      <span class="x">X</span>
      </a>
   </li>
   <li class="option-l" id="option78">
      <a href="javascript:void(0)" name="l" id="swatch78" class="swatch-link swatch-link-180" title="L" style="height: 23px; min-width: 23px;">
      <span class="swatch-label" style="height: 21px; min-width: 21px; line-height: 21px;">
      L                                 </span>
      <span class="x">X</span>
      </a>
   </li>
   <li class="option-xs" id="option81">
      <a href="javascript:void(0)" name="xs" id="swatch81" class="swatch-link swatch-link-180" title="XS" style="height: 23px; min-width: 23px;">
      <span class="swatch-label" style="height: 21px; min-width: 21px; line-height: 21px;">
      XS                                 </span>
      <span class="x">X</span>
      </a>
   </li>
   <li class="option-xl" id="option77">
      <a href="javascript:void(0)" name="xl" id="swatch77" class="swatch-link swatch-link-180" title="XL" style="height: 23px; min-width: 23px;">
      <span class="swatch-label" style="height: 21px; min-width: 21px; line-height: 21px;">
      XL                                 </span>
      <span class="x">X</span>
      </a>
   </li>
</ul>

try the selector //a[@title="XL"] or, if you need more specificity, //ul[@id="configurable_swatch_size"]/descendant::a[@title="XL"]. It’s usually good to set the base of your selector to an ID since they are supposed to be unique on the page. You could also do //ul[@id="configurable_swatch_size"]//a[@title="XL"] but I’ve run into some scenarios (I don’t know why) where // does not work. So I just standardize on descendant::elementName.

Fin

Hopefully this gives you some hope that browser testing is doable. And hopefully you decide that Magium is the way to do it.