Robot Framework SeleniumLibrary: Complete Guide to Browser Automation
SeleniumLibrary is the bridge between Robot Framework's readable test syntax and Selenium WebDriver's browser automation capabilities. It wraps hundreds of WebDriver operations into keyword-friendly interfaces, handles common pain points like implicit waits, and integrates cleanly with Robot Framework's lifecycle hooks for setup, teardown, and failure handling.
This guide walks through everything a QA engineer needs to go from a bare installation to a production-ready browser automation suite — including the configuration decisions that make the difference between a flaky suite and a stable one.
Installation and Initial Setup
Start with a clean virtual environment to avoid dependency conflicts:
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install robotframework
pip install robotframework-seleniumlibrary
pip install webdriver-manager # Handles chromedriver/geckodriver automaticallyWith webdriver-manager, you no longer need to manually download and version-match browser drivers. Manage it in a conftest-style setup resource:
# resources/browser_setup.resource
*** Settings ***
Library SeleniumLibrary timeout=10s implicit_wait=0s run_on_failure=Capture Page Screenshot
*** Variables ***
${BROWSER} chrome
${BASE_URL} https://app.example.com
${HEADLESS} ${FALSE}
*** Keywords ***
Open Test Browser
[Documentation] Opens a browser with standard test configuration.
${options}= Evaluate sys.modules['selenium.webdriver'].ChromeOptions() sys
Run Keyword If ${HEADLESS} Call Method ${options} add_argument --headless=new
Call Method ${options} add_argument --no-sandbox
Call Method ${options} add_argument --disable-dev-shm-usage
Call Method ${options} add_argument --window-size\=1920,1080
Create Webdriver Chrome options=${options}
Set Window Size 1920 1080
Set Selenium Speed 0s
Close Test Browser
Close All BrowsersThe two most important SeleniumLibrary initialization arguments are timeout (the default wait time for all Wait Until keywords) and implicit_wait. Keep implicit_wait at 0s — mixing implicit and explicit waits produces confusing behavior where Selenium waits twice. Use explicit waits everywhere instead.
run_on_failure=Capture Page Screenshot is a lifesaver: whenever any keyword fails, Robot Framework automatically screenshots the current browser state before propagating the failure. This alone cuts debugging time dramatically.
Locator Strategies
SeleniumLibrary supports every Selenium locator strategy through a prefix syntax. Understanding when to use each is one of the most important skills in browser automation.
CSS Selectors — The Default Choice
*** Keywords ***
Submit Login Form
Input Text css:input[name="email"] ${EMAIL}
Input Text css:input[type="password"] ${PASSWORD}
Click Button css:button[data-testid="login-submit"]CSS selectors are fast, readable, and supported everywhere. Prefer data-testid attributes when your development team adds them — they're stable and semantically clear. Avoid selectors tied to visual styling (css:.bg-blue-500.rounded-lg) because they break on design changes.
XPath — For What CSS Can't Express
*** Keywords ***
Click Edit Button For Product
[Arguments] ${product_name}
Click Element xpath://tr[td[text()="${product_name}"]]//button[contains(@class,"edit")]XPath shines when you need to locate an element by its relationship to content — find the edit button in the same row as a specific product name. Avoid XPath for simple attribute matching; CSS handles that with less syntax.
Accessible Locators — id, name, link text
Click Element id:submit-order
Click Link xpath://a[contains(text(),"View Details")]
Select From List By Label name:country United StatesID-based locators are the fastest and most reliable. Use them whenever the application provides stable IDs. name attributes are common on form inputs. Link text is readable but fragile if link wording changes.
Custom Locators with JavaScript
For custom UI components that don't expose standard attributes:
*** Keywords ***
Click Custom Dropdown Option
[Arguments] ${option_text}
Execute Javascript
... document.querySelector('.custom-select').shadowRoot
... .querySelector('[data-value="${option_text}"]').click()Use JavaScript sparingly — it bypasses Robot Framework's retry logic and can trigger events differently than real user interactions.
Waiting Strategies: Eliminating Flakiness
The single biggest source of flaky Selenium tests is improper waiting. SeleniumLibrary provides a full set of explicit wait keywords:
*** Keywords ***
Wait For Page To Load After Navigation
Wait Until Element Is Visible css:[data-testid="page-content"] timeout=15s
Wait Until Element Is Enabled css:[data-testid="primary-action"]
Wait For Ajax Request To Complete
Wait Until Element Is Not Visible css:.loading-spinner timeout=30s
Wait Until Element Contains css:[data-testid="results-count"] result
Wait For Element With Polling
Wait Until Keyword Succeeds 30s 2s
... Element Should Contain css:[data-testid="status-badge"] CompletedWait Until Element Is Visible waits for the element to exist in the DOM and have a non-zero size. This handles elements that are added dynamically but may not be present at page load.
Wait Until Element Is Not Visible is crucial for loading states. After triggering an async action, wait for the loader to disappear before asserting on results.
Wait Until Keyword Succeeds is the most flexible: it retries any keyword on a configurable interval until it passes or the timeout expires. Use it for polling patterns like "keep checking the status badge until it says Completed."
Page Load Waiting
After navigation, wait for a specific element that indicates the page is ready — not for a fixed number of seconds. Fixed sleeps make tests slow when the page is fast and flaky when the server is slow.
*** Keywords ***
Go To Orders Page
Go To ${BASE_URL}/orders
Wait Until Element Is Visible css:[data-testid="orders-table"] timeout=20s
# Also wait for the table to have content, not just be visible
Wait Until Element Is Not Visible css:[data-testid="orders-skeleton"] timeout=20sBrowser Management
For suites with multiple test cases, the browser lifecycle decisions have a significant impact on both speed and test isolation.
Suite-Level Browser (Fast, Less Isolated)
*** Settings ***
Suite Setup Open Browser And Authenticate
Suite Teardown Close All Browsers
*** Keywords ***
Open Browser And Authenticate
Open Test Browser
Go To ${BASE_URL}/login
Log In As Admin User
Save Browser State ${TEMPDIR}/auth_stateOne browser instance across the entire suite runs fastest. The tradeoff is that test state can leak between cases. Mitigate this by resetting application state through the API rather than through the UI.
Test-Level Browser (Isolated, Slower)
*** Settings ***
Test Setup Open Test Browser
Test Teardown Close All BrowsersA fresh browser for each test guarantees isolation — cookies, local storage, and session state start clean. This is slower (browser startup takes 1-3 seconds per test) but eliminates entire categories of test interdependency bugs.
Hybrid: Session Persistence
A practical middle ground: open the browser once per suite and restore authenticated session state between tests.
*** Keywords ***
Reset To Authenticated State
Delete All Cookies
Execute Javascript window.localStorage.clear()
Go To ${BASE_URL}/login
${has_session}= Run Keyword And Return Status
... Restore Authenticated Session
Run Keyword Unless ${has_session}
... Authenticate And Save SessionScreenshot on Failure
SeleniumLibrary's run_on_failure hook automatically captures screenshots when keywords fail. Configure a meaningful output directory:
*** Settings ***
Library SeleniumLibrary
... timeout=10s
... implicit_wait=0s
... run_on_failure=Custom Failure Handler
*** Keywords ***
Custom Failure Handler
${timestamp}= Get Current Date result_format=%Y%m%d_%H%M%S
${test_name}= Get Variable Value ${TEST NAME}
${filename}= Set Variable failure_${test_name}_${timestamp}.png
Capture Page Screenshot ${OUTPUT DIR}/screenshots/${filename}
Log Screenshot saved: ${filename} WARNRobot Framework's ${OUTPUT DIR} variable resolves to the directory where test results are being written — typically the results/ directory you pass with --outputdir. Screenshots taken here are automatically embedded in the HTML report when you reference them with Log.
Headless Mode for CI
Always run headless in CI environments. The configuration above handles this through the ${HEADLESS} variable:
# CI command
robot --variable HEADLESS:True --outputdir results tests/A GitHub Actions example:
- name: Run Robot Framework Tests
run: |
pip install -r requirements.txt
robot \
--variable HEADLESS:True \
--variable BASE_URL:${{ vars.STAGING_URL }} \
--outputdir results \
--loglevel DEBUG \
tests/
env:
DISPLAY: :99
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: robot-results
path: results/
retention-days: 30The if: always() on the artifact upload step is essential — you want the results even when tests fail. Without it, CI hides the evidence you need to debug failures.
Complete Example: Login Test Suite
Putting it all together:
# tests/auth/login_tests.robot
*** Settings ***
Resource ../../resources/browser_setup.resource
Resource ../../resources/pages/login_page.resource
Suite Setup Open Test Browser
Suite Teardown Close All Browsers
Test Setup Navigate To Login Page
Test Tags auth
*** Test Cases ***
Valid Credentials Grant Access
[Tags] smoke
Enter Email valid@example.com
Enter Password SecurePass123!
Submit Login Form
Dashboard Should Be Visible
Invalid Password Shows Error Message
Enter Email valid@example.com
Enter Password wrongpassword
Submit Login Form
Error Message Should Contain Invalid email or password
Empty Email Field Shows Validation Error
Clear Field And Submit Login
Validation Error Should Appear On email
Account Lockout After Five Failed Attempts
[Tags] security
Attempt Login Five Times With Wrong Password valid@example.com
Error Message Should Contain Account temporarily locked
Account Lock Email Should Be Sent To valid@example.com# resources/pages/login_page.resource
*** Settings ***
Library SeleniumLibrary
*** Variables ***
${LOGIN_URL} /login
${EMAIL_FIELD} css:input[data-testid="email-input"]
${PASS_FIELD} css:input[data-testid="password-input"]
${SUBMIT_BTN} css:button[data-testid="login-submit"]
${ERROR_MSG} css:[data-testid="auth-error"]
*** Keywords ***
Navigate To Login Page
Go To ${BASE_URL}${LOGIN_URL}
Wait Until Element Is Visible ${EMAIL_FIELD}
Enter Email
[Arguments] ${email}
Clear Element Text ${EMAIL_FIELD}
Input Text ${EMAIL_FIELD} ${email}
Enter Password
[Arguments] ${password}
Clear Element Text ${PASS_FIELD}
Input Text ${PASS_FIELD} ${password}
Submit Login Form
Click Element ${SUBMIT_BTN}
Wait Until Element Is Not Visible css:.loading-overlay timeout=10s
Dashboard Should Be Visible
Wait Until Element Is Visible css:[data-testid="dashboard-header"] timeout=15s
Error Message Should Contain
[Arguments] ${expected_text}
Wait Until Element Is Visible ${ERROR_MSG} timeout=5s
Element Should Contain ${ERROR_MSG} ${expected_text}When to Move to Playwright
SeleniumLibrary is mature, well-documented, and covers most browser automation needs. But if your application uses Shadow DOM, modern web components, or requires network interception and request mocking, the newer Robot Framework Browser library (based on Playwright) offers native support for these scenarios.
HelpMeTest uses Playwright natively under the hood, which means its AI-generated test suites handle modern web applications that trip up Selenium-based approaches. For greenfield projects, the Playwright-based Browser library is worth evaluating. For existing SeleniumLibrary suites, the migration path is gradual — most keyword patterns translate directly.
Key Takeaways
SeleniumLibrary's reliability comes from consistent patterns: CSS selectors with data-testid attributes, explicit waits instead of sleeps, run_on_failure screenshots, and clear browser lifecycle management. The test cases above are readable by anyone on the team — developers can understand what's being tested, product managers can verify the scenarios match requirements, and QA engineers can maintain them without fighting framework internals.
Master these patterns and SeleniumLibrary becomes a tool that scales from a handful of smoke tests to a full regression suite covering complex multi-step user journeys.