What to avoid when writing Selenium test scripts?
Selenium makes browser automation easy to get started with. But writing stable, maintainable test scripts is a whole other story.
At first, everything works fine, then a button moves, or a page loads slower than expected, or your script stops right in the middle of a checkout flow. You end up debugging for hours only to realize that the test is brittle. Now imagine that happening across dozens of scripts.
Selenium command scripts are only as reliable as the practices behind them. The wrong habits like hardcoded waits or poor locator choices can turn your tests into time-wasting liabilities. And that’s what we want to help you avoid.
In this article, we’ll walk through six common mistakes testers make when writing Selenium scripts. You’ll see what not to do, and more importantly, what to do instead.
Here’s what we’ll cover:
- Why hardcoded waits break more than they fix
- How to write locators that survive UI changes
- Why page sync issues lead to test flakiness
- How to manage test data the right way
- How to prevent test crashes with error handling
- How to write cleaner, modular scripts that scale
Let’s dive in and start fixing what is often overlooked in Selenium script design.
6 things to avoid when creating Selenium test scripts
1. Using hardcoded waits
Hardcoded waits tell your test to pause for a fixed amount of time. This might look safe, but it often leads to longer runtimes and brittle scripts. If the application loads faster or slower than expected, the script either wastes time or fails unexpectedly.
A better approach is to use explicit waits. These waits allow the test to continue as soon as the element appears or becomes ready. That means your scripts are more responsive and reliable in different environments.
Here is a short before-and-after example.
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
import time
driver = webdriver.Chrome()
driver.get("https://katalon.com")
# BEFORE: fixed wait
time.sleep(5)
banner = driver.find_element(By.ID, "main-banner")
# AFTER: explicit wait
wait = WebDriverWait(driver, 10)
banner = wait.until(EC.visibility_of_element_located((By.ID, "main-banner")))
print("Banner loaded:", banner.text)
driver.quit()
2. Writing locators that are too fragile
Locators define how your test finds an element, so if that locator depends on unstable attributes, it can break every time the UI changes slightly. Long XPath expressions and auto-generated IDs often change between builds or deployments.
To build scripts that last, you should choose locator types that remain consistent over time. Stable locators help you reduce maintenance and improve test stability across releases.
Here are reliable locator types to use:
- ID: Ideal when available, fast and specific
- Name: Helpful for form fields and standard inputs
- CSS Selector: Clean, fast, and easy to read
Review your locator strategy before you finalize any script. Look for the simplest, most stable option first. This habit improves every test and aligns with best practices in any Selenium locator strategy.
💡 Read more: Locators in Selenium: How to use them
3. Ignoring page synchronization
Every web application responds in its own time. Some elements appear instantly, while others take a moment. When your script tries to interact with something that is not ready, you may see an error like "NoSuchElementException".
You can handle this with smart waiting. Instead of guessing when the page is ready, wait for a specific condition. One common example is to wait until an element becomes clickable. This gives your test a smooth flow and prevents timing issues.
Here is a simple way to wait before typing into a search box.
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
driver = webdriver.Chrome()
driver.get("https://katalon.com")
wait = WebDriverWait(driver, 10)
search_box = wait.until(EC.element_to_be_clickable((By.ID, "search-bar")))
search_box.send_keys("automation testing")
driver.quit()
Waiting with intention gives your script time to breathe. It also ensures that the next action lands exactly where it should.
💡Read more: How to handle Selenium timeouts?
4. Skipping test data management
Hardcoding values inside your test script works for small demos, but it limits reusability. Each time you want to test a new scenario, you must update the script manually, and that slows you down and increases maintenance.
By using variables or loading data from a file, your script becomes more flexible. You can run the same test logic across multiple data sets without rewriting anything.
Here is a simple comparison:
Approach | Test Value Source | Flexibility |
---|---|---|
Hardcoded | Directly in script | Low |
Parameterized | Variable or external source | High |
You can start simple by storing test inputs in a list or a file the feed them into your scripts as needed.
💡Read more: A guide to test data management
5. Not handling exceptions properly
In every automation suite, things sometimes go off track. A popup appears, a button disappears, or the network lags. With proper exception handling, your test can stay on course and give you useful feedback.
Try-catch blocks give you control when something unexpected happens. You can log the issue, skip to the next step, or retry the action. This makes the suite more resilient and avoids sudden stops.
Here is a simple example that handles a missing element and logs the error.
from selenium.common.exceptions import NoSuchElementException
try:
driver.find_element(By.ID, "promo-banner").click()
except NoSuchElementException as e:
print("Element not found. Skipping promo banner.")
print("Error message:", e)
Logging helps you trace problems after a run. You can store messages in a file, send alerts, or print them in the console. Clear logs give you insight without stopping the flow of the test suite.
6. Overcomplicating scripts
Simple scripts are easier to maintain. When your test logic lives in one long block, it becomes harder to read, debug, and extend. A more effective strategy is to break your scripts into small reusable parts.
Functions help you repeat actions cleanly. Page Object Models give structure to how your script interacts with each page. These techniques make your Selenium automation easier to scale and faster to update.
If you want to follow good Selenium best practices, start with this mindset: build for clarity, not just for function.
Here are signs a script is ready to be modularized:
- You scroll a lot to find what the script is doing
- You copy and paste the same step more than once
- You avoid editing it because the structure is unclear
Breaking things down early saves time later and supports a more maintainable testing process for the whole team.
Why choose Katalon to automate tests?
Katalon is a test automation platform that helps teams move faster without sacrificing stability. It takes everything great in Selenium and makes it easier, smarter, and more productive to manage.
With Katalon, you get:
- A complete environment for designing, organizing, and running automated tests. You can manage locators, data sets, and reports all in one place.
- Out-of-the-box support for multiple browsers, devices, and environments. You can run tests locally, on cloud services, or inside CI pipelines with zero extra setup.
- Smart wait handling and locator recovery powered by AI. Your tests stay stable even as the UI changes over time.
- Powerful analytics and reporting dashboards that show you test health, performance, and coverage at a glance.
- Built-in integrations with your favorite tools like Jenkins, GitHub, Azure DevOps, and Jira. Everything flows from commit to deploy with confidence.
In short, Katalon reduces the effort of maintaining Selenium test scripts. It gives teams a smarter way to scale automation without slowing down.
📝 Want to explore what Katalon can do for your team? Request a demo to see how it helps teams automate faster with less effort.
