Protractor end-to-end tests failing intermittently

Loading

Protractor is a popular end-to-end (E2E) testing framework for Angular applications. However, one of the most frustrating issues with Protractor tests is intermittent failures—tests that pass sometimes and fail randomly.

Common Symptoms:

  • Tests fail inconsistently, even without code changes.
  • Timeout errors occur randomly.
  • Elements are not found, even though they exist.
  • Tests work locally but fail on CI/CD pipelines.
  • Clicking or typing actions fail sporadically.

In this guide, we will explore why Protractor tests fail intermittently and provide step-by-step fixes.


1. Why Do Protractor Tests Fail Intermittently?

1.1. The Application is Not Fully Loaded Before Interacting

Protractor waits for Angular to finish rendering, but sometimes asynchronous operations (like HTTP requests) take longer.

Example Failure:

element(by.id('submitBtn')).click(); //  Fails: Button is not yet clickable

Fix: Use browser.wait() to Ensure the Button is Clickable

let EC = protractor.ExpectedConditions;
let submitBtn = element(by.id('submitBtn'));

browser.wait(EC.elementToBeClickable(submitBtn), 5000); // Wait up to 5 seconds
submitBtn.click();

Best Practice: Always wait for elements to be ready before interacting with them.


1.2. Elements Load Dynamically or Are Removed Too Quickly

Sometimes, elements load dynamically, causing Protractor to look for them before they exist.

Example Failure:

element(by.css('.notification')).getText(); //  Fails: Element loads too late

Fix: Use ExpectedConditions.presenceOf()

let notification = element(by.css('.notification'));
browser.wait(EC.presenceOf(notification), 5000); // Wait until element appears
expect(notification.getText()).toContain('Success');

1.3. Hardcoded browser.sleep() is Unreliable

browser.sleep(5000); forces the test to wait, but it does not guarantee the page is ready.

Bad Practice:

browser.sleep(5000); //  May be too short or too long
element(by.id('loginBtn')).click();

Fix: Use ExpectedConditions Instead

let loginBtn = element(by.id('loginBtn'));
browser.wait(EC.elementToBeClickable(loginBtn), 5000);
loginBtn.click();

1.4. Tests Fail Due to Animations

Animations can cause race conditions, where an element exists but is not yet interactable.

Example Failure:

let menu = element(by.id('menu'));
menu.click(); // Fails: Menu animation is still running

Fix: Disable Animations in protractor.conf.js

onPrepare: function() {
browser.executeScript('window.sessionStorage.clear();');
browser.executeScript('window.localStorage.clear();');
browser.executeScript('document.body.style.animation = "none";');
}

This ensures animations do not interfere with test execution.


1.5. Clicking on the Wrong Element Due to Overlapping Elements

If multiple elements match a selector, Protractor may click the wrong one.

Example Failure:

element(by.buttonText('Submit')).click(); // ❌ Clicks the wrong "Submit" button

Fix: Use a More Specific Selector

element(by.css('#form-container button[type="submit"]')).click();

Fix: Ensure the Element is Visible Before Clicking

browser.wait(EC.visibilityOf(element(by.css('.submit-btn'))), 5000);
element(by.css('.submit-btn')).click();

1.6. Async Code Not Handled Properly

If your test contains async operations but doesn’t use await, the test may fail randomly.

Incorrect Async Handling:

it('should submit form', function() {
element(by.id('username')).sendKeys('testUser');
element(by.id('password')).sendKeys('password');
element(by.id('loginBtn')).click(); // May run before previous actions complete
expect(element(by.id('welcomeMessage')).getText()).toContain('Welcome');
});

Fix: Use async/await for Proper Execution Order

it('should submit form', async function() {
await element(by.id('username')).sendKeys('testUser');
await element(by.id('password')).sendKeys('password');
await element(by.id('loginBtn')).click();

let welcomeMessage = element(by.id('welcomeMessage'));
await browser.wait(EC.visibilityOf(welcomeMessage), 5000);
expect(await welcomeMessage.getText()).toContain('Welcome');
});

1.7. Tests Fail in CI/CD Due to Headless Mode

Running tests in headless Chrome can cause unexpected failures.

Headless Mode in CI/CD:

capabilities: {
browserName: 'chrome',
chromeOptions: {
args: ['--headless', '--disable-gpu']
}
}

Fix: Add Flags to Improve Stability

capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [
'--headless',
'--disable-gpu',
'--no-sandbox',
'--disable-dev-shm-usage',
'--window-size=1920,1080'
]
}
}

1.8. Handling Stale Element Reference Errors

If an element disappears and reappears, Protractor may throw a stale element error.

Example Failure:

let saveBtn = element(by.id('save'));
saveBtn.click(); // Fails: Element reference is stale

Fix: Re-Find the Element Before Clicking

let saveBtn = element(by.id('save'));
browser.wait(EC.presenceOf(saveBtn), 5000);
saveBtn = element(by.id('save')); // Find it again
saveBtn.click();

2. Summary of Fixes

IssueSolution
Page not fully loaded before actionUse ExpectedConditions to wait
Dynamic elements loading lateUse presenceOf()
Hardcoded browser.sleep()Use ExpectedConditions instead
Animations delaying interactionsDisable animations in onPrepare
Clicking wrong elementUse a more specific selector
Async operations causing failuresUse async/await properly
Tests failing in headless ChromeAdd flags like --no-sandbox
Stale element reference errorRe-find the element before interacting

Leave a Reply

Your email address will not be published. Required fields are marked *