Wed 04 Aug 2010
Measuring the speed of how long a page takes to load is becoming one of the most important metrics that
we as software developers or testers can measure. This is because milliseconds can mean the difference between
a sale being paid for or abandoned. A couple of years ago Amazon did research that showed for every 100ms slower
the page was in loading they lost 1% of sales.
The downside to all of this is that we, as developers, testers and even ops, can only control the speed of things
within our code and within our datacenters. With this in mind we should be trying to record all of the information
that we can from within the browser. Last year at GTAC 2009 (
presentation video) David Henderson and I discussed how we used YSlow and Selenium to record page metrics and load
times of the web application. We made the total primed page size decrease by 85% with the work we did.
The unfortunate thing about the way we did this was that we had to hack YSlow because it didn't give us all the information
that we wanted. We also needed to hack Selenium because it blocked a few of the headers to do with element expiry. This
is not really ideal but with the new Web Timings API we are starting
to be able to do all of this with just a bit of JavaScript.
To access this we need to get hold of the performance object and then get it to return a dictionary of all the timing data.
The values that are returned are milliseconds from 1/1/1970. They give you all cool bits of information like how long it
takes to unload a page you're on before navigating to the new page and how long that takes to load. It also tells you how long
domain lookups take and how long redirects take. These are all neat tidbits of information when performance tuning your web application.
So below is a C# extension method that I have created that extends the WebDriver object to collect the information for you. Not all
browsers support this at the moment but as web timings becomes the standard the code will start returning data. Current browsers that
support this are Chrome 6 and IE9 pre3. A patch has been submitted to Mozilla for Firefox 4 so hopefully this will be applied soon.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using OpenQA.Selenium;
namespace AutomatedTester.PagePerf
{
public static class Extensions
{
public static Dictionary<string,object> WebTimings(this IWebDriver driver)
{
var webTiming = (Dictionary<string, object>)((IJavaScriptExecutor)driver)
.ExecuteScript(@"var performance = window.performance || window.webkitPerformance || window.mozPerformance || window.msPerformance || {};
var timings = performance.timing || {};
return timings;");
/* The dictionary returned will contain something like the following.
* The values are in milliseconds since 1/1/1970
*
* connectEnd: 1280867925716
* connectStart: 1280867925687
* domainLookupEnd: 1280867925687
* domainLookupStart: 1280867925687
* fetchStart: 1280867925685
* legacyNavigationStart: 1280867926028
* loadEventEnd: 1280867926262
* loadEventStart: 1280867926155
* navigationStart: 1280867925685
* redirectEnd: 0
* redirectStart: 0
* requestEnd: 1280867925716
* requestStart: 1280867925716
* responseEnd: 1280867925940
* responseStart: 1280867925919
* unloadEventEnd: 1280867925940
*/
return webTiming;
}
}
}
Area: blog
Mon 05 Jul 2010
First let me start by thanking those who followed the Selenium StackExchange Proposal. We managed to meet all the criteria that was set by the StackExchange Team and have moved onto the second stage.
The second stage is the commitment stage. This is where we get people to commit that they are going to use it to ask questions or answer questions when they are put on the site. This is a very important stage because it shows how strong the Selenium Community is and in my opinion we do have a strong community. Unfortunately, my opinion doesn't matter that much in the StackExchange process so if you haven't committed to the Selenium StackExchange Site please click on the link and commit.
You never know when you may need an answer to a question that may already have been asked and it's easy available to you with the new site
Area: blog
Tue 22 Jun 2010
A question I get asked regularly, and is regularly asked on forums I visit, is how do "I make my tests maintainable?".
This is normally from a tester upset that the developers are commenting out tests because they feel its too much work to update the tests.
The next popular question is "how can I get my non-technical colleagues creating automated tests?". For me the answer to both is very similar.
Make the tests self-documenting and easy to read (maybe using a DSL). You, as a developer, would do this with your production code so why not do it with your tests.
Now, what do I mean by self-documenting and easy to read?
- Methods should explain what they are for. This goes for test and production.
I like to format my test methods like ShouldDoX. This is similar to what BDD practioners do for their tests.
If another prerson comes along to look at some code that you have written, and your tests,
they will be able to see what you meant it to be doing.
public void ShouldLoginAndChangeStatus()
for a social networking app explains exactly what they are expecting.
- Methods and tests should be succinct
Methods that are huge, in test and production, are likely to have bugs in them because people have to
scroll to get to the bottom. They are likely to have business logic bugs when that happens. I also like abstract
as much as I can so that it makes the code readable.
Which is better for readability?
selenium.click("//a[contains(@blah,'someIdButNotAllOfIt)]")
or
// linkToCoolStuff is a constant holding the XPath above
selenium.click(linkToCoolStuff)
-
Maybe using a DSL?
We could take this one step further and create a DSL so that we do not have to care how it does it, we just know it will happen.
For UI testing I like the idea of the Page Object design pattern for creating tests.
You split the UI and the tests apart so that you can have a test looks like the following.
[Test]
public void ShouldLoadHomeThenGoToXpathTutorial()
{
Home home = new Home(selenium);
SeleniumTutorials seleniumTutorials = home.ClickSelenium();
SeleniumXPathTutorial seleniumXPathTutorial = seleniumTutorials.ClickXpathTutorial();
Assert.True(seleniumXPathTutorial.
IsInputOnScreen(SeleniumXPathTutorial.FirstInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.SecondInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.Total));
}
I have created a tutorial on how to use the Page Object Pattern with Selenium
to make tests that are extremely maintainable and also allows non-technical people to write tests without having to understand how Selenium works.
Area: blog
Tue 22 Jun 2010
In this tutorial we will have a look at how we can apply the Page Object design pattern.
This is an extremely useful design pattern for UI tests as it abstracts away how we get to different pages or screens.
Page Object design helps us create a DSL for our tests that if something were to change on the page we don't need to change the test, we just need
to update the object that represents the page.
A large number of people prefer to write tests in this manner than having to use Selenium IDE. I have to admit that I am one these people. It does require a bit of up front
work but once done non-technical can start writing tests in a easy to use manner and never have to understand how everything holds together.
The following example is done in C# and requires that you have NUnit and at least Visual Studio Express installed.
In the example below I have created my Page Objects to work against my site.
- Create a class file for a page. In this case let's start on the Home Page. When this object is instantiated we are going to navigate to that page. From there we
public class Home
{
private readonly ISelenium _selenium;
///
/// Instantiates a new Home Page object. Pass in the Selenium object created in the test SetUp().
/// When the object in instantiated it will navigate to the root
///
/// Selenium Object created in the tests
public Home(ISelenium selenium)
{
this._selenium = selenium;
if (!selenium.GetTitle().Contains("home"))
{
selenium.Open("/");
}
}
///
/// Navigates to Selenium Tutorials Page. Selenium object wll be passed through
///
/// SeleniumTutorials representing the selenium_training.htm
public SeleniumTutorials ClickSelenium()
{
_selenium.Click("link=selenium");
_selenium.WaitForPageToLoad("30000");
return new SeleniumTutorials(_selenium);
}
///
/// Click on the blog or blog year and then wait for the page to load
///
/// blog or blog year
/// Object representing /blog.* pages
public Blog ClickBlogYear(string year)
{
_selenium.Click("link=" + year);
_selenium.WaitForPageToLoad("30000");
return new Blog(_selenium);
}
// Add more methods as you need them
}
-
Let's create another object that will represent one of the tutorials on the site
public class SeleniumXPathTutorial
{
private readonly ISelenium _selenium;
public const string FirstInput = "number1";
public const string SecondInput = "number2";
public const string Total = "total";
public SeleniumXPathTutorial(ISelenium selenium)
{
this._selenium = selenium;
}
public bool IsInputOnScreen(string locator)
{
return _selenium.IsElementPresent(locator);
}
}
- Let us now create a test class that will hold all of the tests. We will need to create an object for each of the pages and then use the methods in them our tests.
As we can see from the code below, the Selenium parts have been kept to a minimum. In actual fact the only bit that shows Selenium is in the Setup() and Teardown() methods.
[TestFixture]
public class SiteTests
{
private ISelenium selenium;
[SetUp]
public void Setup()
{
selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://www.theautomatedtester.co.uk");
selenium.Start();
}
[TearDown]
public void Teardown()
{
selenium.Stop();
}
[Test]
public void ShouldLoadHomeThenGoToXpathTutorial()
{
Home home = new Home(selenium);
SeleniumTutorials seleniumTutorials = home.ClickSelenium();
SeleniumXPathTutorial seleniumXPathTutorial = seleniumTutorials.ClickXpathTutorial();
Assert.True(seleniumXPathTutorial.
IsInputOnScreen(SeleniumXPathTutorial.FirstInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.SecondInput));
Assert.True(seleniumXPathTutorial
.IsInputOnScreen(SeleniumXPathTutorial.Total));
}
}
- Now try make a few more tests to try work against the other pages in the site. If you find that you are repeating yourself in the classes split that out again. If you follow
DRY (Don't Repeat Yourself) then you will have a set of tests that are extremely maintainable.
By using the Page Object you can make your tests really easy to write and when things change, as they invariably do, you can go from a number of tests
failing to suddenly passing with making one change. We have followed good practise in keeping the test code (the asserts) inside the test and not in the page objects.
You can also swap out Selenium 1 and put Selenium 2 in its place and not touch the tests.
Area: tutorials
Fri 11 Jun 2010
A while ago Dave Hunt, a Selenium Contributor and London Selenium User Group organiser, suggested that perhaps we should have a look at creating a StackExchange site for Selenium. He suggested it after the StackOverflow team announced changes to StackExchange
On Wednesday Dave created our proposal so that we can get the ball rolling. I have signed up and want to help this proposal as much as I can. There are a couple reasons why I am advocating this so much.
- Its a lot friendlier to use than Google groups
- It will show up possible duplicates in questions. Chances are that if you are having an issue, someone else has had that issue before
- It's like a game! It's points based so people will try get the most points that they can.
So what can you as a reader of this blog do? Well, if you are a Selenium User I suggest following to the proposal by logging into Area51 and clicking the follow button. The next thing to do is add some example questions if they aren't there. The more example questions the better as it will give people a better idea of what the StackExchange will be used for. Finally, start voting on the example questions. You do this by clicking on one three links on the left of the question as shown in the image below.

I have benefitted from using the StackOverflow type sites(StackOverflow, ServerFault, etc) since their launch and I think that as a community we can all benefit from this site so please help the cause
Area: blog