Protractor tests inconsistently passing / failing

2019-03-09 03:19发布

My Protractor e2e tests are inconsistently passing and failing.

It seems this could be due to asynchronous javascript, as discussed here: Protractor : How to wait for page complete after click a button?.

However, here it's mentioned that Protractor tests automatically execute sequentially / synchronously: https://github.com/angular/protractor/issues/909

My test script:

describe('Login', function() {

  var ptor;

  beforeEach(function() {
    browser.get('https://127.0.0.1:8443');
    ptor = protractor.getInstance();
    element(by.id('splash')).click();
    browser.ignoreSynchronization = true;  // <-- to proceed beyond splash screen
  });

  describe('with correct email and password', function() {

    beforeEach(function() {
        element(by.id('email')).sendKeys('admin@email.com');
        element(by.id('password')).sendKeys('adminpassword');
        element(by.id('loginButton')).click();
    });

    afterEach(function() {
        ptor.findElement(by.id('logout')).then(function(elem) {
            elem.click();
        });
    });

    it('does not show alert', function() {  // <-- sometimes passes, sometimes fails
        expect(browser.isElementPresent(by.css('.alert-danger'))).toBe(false);
    });

    it('changes route to /admin', function() {  // <-- sometimes passes, sometimes fails
        expect(browser.getCurrentUrl()).toMatch(/\/admin/);
    });
  });
});

In the two tests above, either both tests will pass, or one/both of the tests will fail with these messages:

Failures:

1) Login with correct email and password does not show alert
Message:
  NoSuchElementError: no such element
...
==== async task ====
WebDriver.findElement(By.id("logout"))
...

or

Failures:

1) Login with correct email and password changes route to /admin
Message:
  NoSuchElementError: no such element
...
==== async task ====
WebDriver.findElement(By.id("logout"))
...

Thoughts / help much appreciated.

3条回答
Melony?
2楼-- · 2019-03-09 03:30

browser.ignoreSynchronization = true; has a global effect for all your tests. you may have to set it back to false, so protractor waits for angular to be finished rendering the page. e.g. in or before your second beforeEach function

查看更多
姐就是有狂的资本
3楼-- · 2019-03-09 03:31

I was able to resolve the issue based on the following:

As mentioned by Nguyen Vu Hoang's comment to the original question, I am testing a pure Angular app with what I think is pure Protractor (no webdriver calls). I know ptor.ignoreSynchronization=true should not be required in this case, but for some reason, the tests are not proceeding at button click without this setting.

My new spec:

describe('Login', function() {

  var ptor;

  beforeEach(function() {
    ptor = protractor.getInstance();
    ptor.ignoreSynchronization = true;
    ptor.waitForAngular();
    ptor.get('https://127.0.0.1:8443');
    ptor.findElement(by.id('splash')).then(function(elem) {
        elem.click();
    });
  });

  describe('with correct email and password', function() {

    beforeEach(function() {
        ptor.findElement(by.id('email')).then(function(elem) {
            elem.sendKeys('admin@email.com');
        });

        ptor.findElement(by.id('password')).then(function(elem) {
            elem.sendKeys('adminpassword');
        });

        ptor.findElement(by.id('loginButton')).then(function(elem) {
            elem.click();
        });
    });

    afterEach(function() {
        ptor.findElement(by.id('logout')).then(function(elem) {
            elem.click();
        });
    });

    it('does not show alert', function() {
        expect(ptor.isElementPresent(by.css('.alert-danger'))).toBe(false);
    });

    it('changes route to /admin', function() {
        expect(ptor.getCurrentUrl()).toMatch(/\/admin/);
    });
  });
});
查看更多
Rolldiameter
4楼-- · 2019-03-09 03:47

There is also an another technique to make your tests more stable: Explicit Waits and Expected Conditions (docs).

I've found using Expected Conditions especially useful when testing against non-angular pages or angular applications that have a lot of animations involved.

For example, you can wait for an element to be clickable before making a click:

var EC = protractor.ExpectedConditions;
var link = element(by.id("mylink"));

browser.wait(EC.elementToBeClickable(link), "10000", "The link is still not clickable");
link.click();

There are also other built-in Expected Conditions, such as:

  • presenseOf()
  • visibilityOf()
  • alertIsPresent()
  • textToBePresentInElementValue()
  • etc

And, it is easy to write a custom Expected Condition, example use case:

You can also combine Expected Conditions using and, or and not, e.g.:

var urlChanged = function() {
  return browser.getCurrentUrl().then(function(url) {
    return url != 'http://www.angularjs.org';
  });
};

// condition to wait for url to change, title to contain 'foo', and $('abc') element to contain text 'bar'
var condition = EC.and(urlChanged, EC.titleContains('foo'), EC.textToBePresentInElement($('abc'), 'bar'));
$('navButton').click();
browser.wait(condition, 5000); //wait for condition to be true.
查看更多
登录 后发表回答