Robot Framework SeleniumLibrary: Complete Guide to Browser Automation

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 automatically

With 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 Browsers

The 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.

Click Element    id:submit-order
Click Link       xpath://a[contains(text(),"View Details")]
Select From List By Label    name:country    United States

ID-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"]    Completed

Wait 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=20s

Browser 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_state

One 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 Browsers

A 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 Session

Screenshot 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}    WARN

Robot 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: 30

The 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.

Read more

Start now free