How can I make my Selenium tests less brittle?

You may or may not be able to write tests that are resilient to refactoring. Here's how to make the refactoring less painful: Continuous integration is essential.

Run them every day or every build. The sooner it's fixed, the easier.

Ensure devs can run the tests themselves. Again, the sooner it's seen and fixed, the easier.

Keep selenium tests few. They should focus on critical path / pri 1 test scenarios. Deep testing should be done at unit test level (or jsunit tests). Integration tests are always expensive and less valuable.


I've found PageObject pattern very helpful.

http://code.google.com/p/webdriver/wiki/PageObjects

more info: - What's the Point of Selenium? - Selenium Critique

maybe a good way to start is to incrementally refactor your test cases.

I use the same scenario you have selenium + c#

Here is how my code looks like:

A test method will look like somethink like this

    [TestMethod]
    public void RegisterSpecialist(UserInfo usrInfo, CompanyInfo companyInfo)
    {
        var RegistrationPage = new PublicRegistrationPage(selenium)
              .FillUserInfo(usrInfo)
              .ContinueSecondStep();
        RegistrationPage.FillCompanyInfo(companyInfo).ContinueLastStep();
        RegistrationPage.FillSecurityInformation(usrInfo).ContinueFinishLastStep();
        Assert.IsTrue(RegistrationPage.VerifySpecialistRegistrationMessagePayPal());
        selenium.WaitForPageToLoad(Resources.GlobalResources.TimeOut);
        paypal.LoginSandboxPage(usrInfo.sandboxaccount, usrInfo.sandboxpwd);
        Assert.IsTrue(paypal.VerifyAmount(usrInfo));
        paypal.SubmitPayment();
        RegistrationPage.GetSpecialistInformation(usrInfo);
        var bphome = new BPHomePage(selenium, string.Format(Resources.GlobalResources.LoginBPHomePage, usrInfo.AccountName, usrInfo.Password));
        Assert.IsTrue(bphome.VerifyPageWasLoaded(usrInfo));
        Assert.IsTrue(bphome.VerifySpecialistProfile());
        bphome.Logout();
    }

A page Object will be something like this

public class PublicRegistrationPage
{
    public ISelenium selenium { get; set; }

    #region Constructors
    public PublicRegistrationPage(ISelenium sel)
    {
        selenium = sel;
        selenium.Open(Resources.GlobalResources.PublicRegisterURL);
    }
    #endregion
    #region Methods

    public PublicRegistrationPage FillUserInfo(UserInfo usr)
    {
        selenium.Type("ctl00_cphComponent_ctlContent_wizRegister_tUserFirstName", usr.FirstName);
        selenium.Type("ctl00_cphComponent_ctlContent_wizRegister_tUserLastName", usr.LastName);
        selenium.Select("ctl00_cphComponent_ctlContent_wizRegister_ddlUserCountry", string.Format("label={0}",usr.Country ));
        selenium.WaitForPageToLoad(Resources.GlobalResources.TimeOut);
        selenium.Type("ctl00_cphComponent_ctlContent_wizRegister_tUserEmail", usr.Email );
        selenium.Type("ctl00_cphComponent_ctlContent_wizRegister_tUserDirectTel", usr.DirectTel);
        selenium.Type("ctl00_cphComponent_ctlContent_wizRegister_tUserMobile", usr.Mobile);
        return this;
    }

}

Hope this helps.


Hooking up on any low-level concepts like XPaths, CSS Selectors or IDs for end-to-end tests is a recipe for unstable tests. I advise using testRigor to produce tests that won't break any time you run/change/improve your application a little bit. The code analogous to the page object one above would look like this:

    enter "Peter" into "First Name"
    enter "Pen" into "Last Name"
    enter "US" into "Country" below "User Data"
    enter stored value "email" into "Email"
    enter stored value "password" into "Password"
    enter "415-123-4567" into "Direct Telephone"
    enter "415-123-4568" into "Mobile Number"
    click "Submit"

testRigor would associate texts that look like labels with the inputs so that as soon as from an end-user's perspective your page would look the same then the testRigor scripts will be green. Here is the doc.

disclaimer: I'm a co-founder of testRigor. I co-founded it because we had those exact issues ourselves.

Hope this helps.


How are you creating your Selenium tests, by recording them and playing them back? What we have done is build an object model around pages so that you call a method like "clickSubmit()" rather than clicking on an id (with a naming convention for these ids), which allows selenium tests to survive many changes.