Retry mechanism for flaky tests in Selenium Python - Build an Automation Script
import unittest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestLoginWithRetry(unittest.TestCase): def setUp(self): self.driver = webdriver.Chrome() self.driver.implicitly_wait(5) # Implicit wait for element presence self.wait = WebDriverWait(self.driver, 10) # Explicit wait def login(self): self.driver.get('https://example.com/login') username_input = self.wait.until(EC.presence_of_element_located((By.ID, 'username'))) password_input = self.driver.find_element(By.ID, 'password') login_button = self.driver.find_element(By.ID, 'loginBtn') username_input.clear() username_input.send_keys('testuser') password_input.clear() password_input.send_keys('Test@1234') login_button.click() # Wait for URL to change to dashboard self.wait.until(EC.url_to_be('https://example.com/dashboard')) def test_login_with_retry(self): max_attempts = 3 for attempt in range(1, max_attempts + 1): try: self.login() current_url = self.driver.current_url self.assertEqual(current_url, 'https://example.com/dashboard') print(f'Login succeeded on attempt {attempt}') break except (AssertionError, Exception) as e: print(f'Attempt {attempt} failed: {e}') if attempt == max_attempts: self.fail(f'Login failed after {max_attempts} attempts') def tearDown(self): self.driver.quit() if __name__ == '__main__': unittest.main()
This test script uses Python's unittest framework with Selenium WebDriver.
setUp: Initializes the Chrome browser and sets implicit and explicit waits.
login method: Opens the login page, fills username and password fields, clicks login, and waits explicitly for the dashboard URL.
test_login_with_retry: Tries to login up to 3 times. If the dashboard URL is reached, it passes. If not, it retries. After 3 failures, the test fails.
tearDown: Closes the browser after the test.
This approach uses explicit waits for reliability and a retry loop to handle flaky failures like slow page loads or temporary glitches.
Now add data-driven testing with 3 different sets of login credentials (valid and invalid) to verify retry mechanism works for each case.
Practice
Solution
Step 1: Understand flaky tests
Flaky tests fail sometimes due to temporary problems like slow page loads or network delays.Step 2: Purpose of retry mechanism
Retry mechanism reruns failed tests to check if failure was temporary, reducing false failures.Final Answer:
To rerun tests that fail due to temporary issues like slow loading -> Option CQuick Check:
Retry mechanism = rerun flaky tests [OK]
- Thinking retry speeds up tests
- Assuming retry ignores failures
- Confusing retry with logging
Solution
Step 1: Check retry decorator structure
def retry(func): def wrapper(): for _ in range(3): try: return func() except Exception: pass return wrapper defines a wrapper function that tries to run the test 3 times catching exceptions.Step 2: Verify retry logic correctness
def retry(func): def wrapper(): for _ in range(3): try: return func() except Exception: pass return wrapper retries on exception and returns on success, which is correct retry behavior.Final Answer:
def retry(func): def wrapper(): for _ in range(3): try: return func() except Exception: pass return wrapper -> Option BQuick Check:
Retry decorator = try 3 times with exception catch [OK]
- Not catching exceptions inside loop
- Infinite loops without exit
- Calling function multiple times without try-except
def retry(func):
def wrapper():
for i in range(2):
try:
return func()
except Exception:
print(f"Retry {i+1} failed")
print("All retries failed")
return wrapper
@retry
def test():
print("Running test")
raise Exception("Fail")
test()Solution
Step 1: Analyze retry loop
The wrapper tries to run test twice. Each time test() prints "Running test" then raises Exception.Step 2: Trace output
On first try, prints "Running test", then exception triggers "Retry 1 failed". Second try repeats same steps, then prints "Retry 2 failed". After loop ends, prints "All retries failed".Final Answer:
Running test Retry 1 failed Running test Retry 2 failed All retries failed -> Option AQuick Check:
Retry prints on each failure, then final failure message [OK]
- Missing repeated 'Running test' print
- Ignoring final 'All retries failed' print
- Assuming retry stops after first failure
def retry(func):
def wrapper():
for _ in range(3):
try:
func()
except Exception:
continue
return wrapperSolution
Step 1: Check retry logic
The wrapper calls func() but never returns its result or breaks loop on success.Step 2: Effect of missing return
Without return, wrapper always completes loop and returns None, so retry never stops on success.Final Answer:
The function does not return the result or stop retrying on success -> Option DQuick Check:
Missing return causes always 3 retries even on success [OK]
- Not returning function result inside try
- Misusing continue in except block
- Ignoring decorator metadata preservation
Solution
Step 1: Check retry count and delay
import time def retry(func): def wrapper(): for i in range(3): try: return func() except Exception: if i < 2: time.sleep(2) else: raise return wrapper retries 3 times with a 2-second sleep between tries except after last attempt.Step 2: Verify exception handling
import time def retry(func): def wrapper(): for i in range(3): try: return func() except Exception: if i < 2: time.sleep(2) else: raise return wrapper raises exception after last retry, correctly stopping retries on failure.Final Answer:
import time def retry(func): def wrapper(): for i in range(3): try: return func() except Exception: if i < 2: time.sleep(2) else: raise return wrapper -> Option AQuick Check:
Retry with delay and raise after last try [OK]
- Not raising exception after last retry
- Missing delay between retries
- Retrying infinitely without limit
Ready to prove it?
Pass the test and this pattern moves unknown
