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
Issue | Solution |
---|---|
Page not fully loaded before action | Use ExpectedConditions to wait |
Dynamic elements loading late | Use presenceOf() |
Hardcoded browser.sleep() | Use ExpectedConditions instead |
Animations delaying interactions | Disable animations in onPrepare |
Clicking wrong element | Use a more specific selector |
Async operations causing failures | Use async/await properly |
Tests failing in headless Chrome | Add flags like --no-sandbox |
Stale element reference error | Re-find the element before interacting |