Removing sleeps from your AngularJS Protractor tests

July 6, 2016

sleep()

The Protractor framework does a great job of shielding you from the inherent difficulties of testing your asynchronous Angular app. Using ControlFlow it keeps the list of pending promises hence executing the test in a logical manner. However this is not always enough. Sometimes the tests outrun browser! In such cases you might just be tempted to put in a browser.sleep(2000) to allow everything to get back on track.

wait()

Sleeps are brittle. Sleeps put your tests at the mercy of changes in your test environments (network speed, machine performance etc). They make your tests slower. From a maintainability point of view they are ambiguous to someone else reading your code. Why were the put there? Is the sleep long enough? They are an acceptance that you don’t really know what is going on with your code and in my opinion should be banned (or at least you should try your best to resist the quick win they give you!). Here are some alternatives to sleep using web drivers wait function….

  1. Use angular’s Expected Conditions

    browser.wait(  
    protractor.ExpectedConditions.visibilityOf(
      element(by.id('header')
    ), 5000);
    
  2. Use web driver directly

    function waitForElement (locator) {  
    browser.driver.wait(function () {
       return browser.driver.isElementPresent(locator);
    }, 5000);
    }
    waitForElement(element(by.id('header'));
    

This function is useful for pages that don’t have angular (say you want to test your login page).

  1. For more custom conditions on a promise you can use the following function

    waitForCondition (promise, testFn) {  
    browser.wait(function () {
       var deferred = protractor.promise.defer();
       promise.then(function (data) {
           deferred.fulfill(testFn(data));
       });
       return deferred.promise;
    }, 5000);
    }
    

For example we have a function in our page object that returns the number of rows in a table (returns as a promise). We call the waitForCondition function as follows:

waitForCondition(pageObject.getNumberRowsInTable(), function (rows) {  
 return rows.length === 1;
});
  1. Finally to wait for the URL to change

    function waitForUrl(expectedUrlFragment){  
    browser.driver.wait(function() {
    return browser.driver.getCurrentUrl().then(function(url) {
    return new RegExp(expectedUrlFragment).test(url);
    });
    }, 5000)};
    

Originally posted on the Poppulo Techblog