Selenium is an open-source automated testing framework used for web application testing and automation. It allows you to control web browsers programmatically and automate actions like clicking links, filling forms, scraping data etc.
Selenium supports all major browsers like Chrome, Firefox, Safari, Edge etc. To control these browsers, Selenium uses browser-specific driver programs. For Firefox, it uses GeckoDriver. In this comprehensive guide, we will learn how to setup Selenium with Firefox driver on Linux and write test automation scripts.
Prerequisites
Before you can start using Selenium with Firefox, you need to have the following set up:
- Linux operating system (Ubuntu 20.04 used here)
- Python 3.8+
- Pip package manager
- Firefox web browser
- Git version control
If you need help with setting these up, refer to the installation guides on LinuxHint.
Setting up Virtualenv
It‘s recommended to create a separate Python virtualenv for your Selenium automation project. This keeps the project dependencies isolated from rest of the system.
To create and activate virtualenv:
$ python3 -m venv seleniumenv
$ source seleniumenv/bin/activate
The prompt will now show the virtualenv name indicating it‘s active.
Installing Selenium Python Package
Selenium Python library can be installed with pip as:
$ pip install selenium
This will download the latest Selenium package and dependencies into the virtualenv.
Downloading GeckoDriver
GeckoDriver is the link between Selenium test code and Firefox browser. You need to download the correct version of GeckoDriver for your Firefox version from GitHub.
Here are the steps to download it on Linux:
$ wget https://github.com/mozilla/geckodriver/releases/download/v0.31.0/geckodriver-v0.31.0-linux64.tar.gz
$ tar -xvzf geckodriver*
$ chmod +x geckodriver
$ mv geckodriver seleniumenv/bin/
This downloads and extracts the latest GeckoDriver binary into the virtualenv‘s bin folder to be accessible globally within virtualenv.
With the prerequisites installed, we are now ready to write some test automation code!
Writing First Selenium Firefox Test
Let‘s start with a simple script to open firefox and print the page title.
Create a file test_sample.py with the following code:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
firefox_driver_path = ‘./seleniumenv/bin/geckodriver‘
driver = webdriver.Firefox(executable_path=firefox_driver_path)
driver.get("https://www.linuxhaxor.net")
print(driver.title)
driver.quit()
Here‘s what it does step-by-step:
- Import Selenium bindings
- Specify path to GeckoDriver executable
- Create new Firefox webdriver instance
- Open LinuxHint homepage
- Print out page title
- Close Firefox browser
Execute the script:
$ python test_sample.py
Linux Hint - Linux Blog
As expected, it opens Firefox, prints title and quits it. With this verification, our Selenium environment is ready!
This was just a simple usage demo. Let‘s move on to more useful examples.
Running Firefox Headless
Headless mode allows running browser without GUI. This is useful for:
- Running tests on remote servers or Docker containers
- Faster execution without rendering UI
- More stability
To activate headless mode, we need to tweak the webdriver initialization:
from selenium.webdriver.firefox.options import Options
firefox_options = Options()
firefox_options.add_argument("--headless")
driver = webdriver.Firefox(executable_path=firefox_driver_path, options=firefox_options)
Rest of the code remains same. Now try running it, and you‘ll see tests execute without opening any Firefox windows.
Setting Page Load Timeout
Sometimes pages may take long time to load due to bad network or heavy content. Selenium waits for ever by default.
We can configure a timeout like 20 seconds for page, after which script will error out:
driver.set_page_load_timeout(20)
Similarly, timeouts can be configured for script/test case level as well. This helps preventing scripts hanging indefinitely.
Taking Screenshots
Visual proof is useful for debugging script failures. Selenium allows capturing screenshots easily like:
driver.save_screenshot(‘page.png‘)
This will save current page image to ‘page.png‘ file.
For debugging a failure, screenshots can be captured right before the exception:
try:
# selenium test
except Exception as e:
driver.save_screenshot(‘error.png‘)
raise
Now error image is available along with stack trace.
Locating Elements
The first step to automate interactions is identifying desired elements uniquely. Selenium provides a range of locator strategies:
- ID
- Name
- Class name
- Tag name
- CSS Selector
- XPath
- Link text
- Partial link text
Among these, ID is most convenient and unique, if available:
element = driver.find_element(By.ID, ‘signup‘)
For elements without ID, CSS Selectors provide flexibility:
element = driver.find_element(By.CSS_SELECTOR, ‘button.signup‘)
Make sure to play around with the console to create working locators.
Working with Inputs
Common automation tasks involve typing text into inputs, filling forms etc.
Let‘s see an example code to login into a website:
email_input = driver.find_element(By.ID, ‘email‘)
email_input.send_keys(‘john@test.com‘)
password_input = driver.find_element(By.ID, ‘password‘)
password_input.send_keys(‘123456‘)
login_button = driver.find_element(By.CSS_SELECTOR, ‘button[type="submit"]‘)
login_button.click()
This locates the input elements, enters texts into them and finally clicks login. Very straight-forward!
We can also clear inputs easily before entering text:
input.clear()
input.send_keys(‘updated text‘)
For use cases like login, we would ideally parameterize the script to pickup credentials dynamically from environment variables or command line arguments.
Interacting with Buttons & Links
Clicking buttons is another common activity for navigation, form submission, ajax calls etc.
Below code snippet logs user out by clicking the logout link:
logout_link = driver.find_element(By.LINK_TEXT, ‘Sign Out‘)
logout_link.click()
We located link using visible text and invoked click action.
Sometimes click may not work if element is dynamically covered or page layout changes. An alternative approach is executing javascript instead:
driver.execute_script("arguments[0].click();", logout_link)
This helps overcoming issues with simple click method.
Handling Dropdowns
Dropdown selection can be automated using selenium select class. Consider this example:
from selenium.webdriver.support.select import Select
dropdown = driver.find_element(By.ID, ‘timezone‘)
select = Select(dropdown)
select.select_by_visible_text(‘Hawaii‘)
selected_option = select.first_selected_option
print(selected_option.text)
This prints selected dropdown value after making a selection.
We created Select wrapper around native dropdown to access useful methods like:
- select_by_visible_text()
- select_by_index()
- select_by_value()
This makes working with selects hassle-free.
Popup Alerts
Website popups and alert dialogs can also be handled easily. Consider this sample alert:
# trigger alert on site
driver.execute_script(‘alert("Hello User");‘)
alert = driver.switch_to.alert
print(alert.text)
alert.accept()
By switching driver context to the alert and calling appropriate methods, we can read alert text and perform actions like accept/dismiss on it.
This works for all JavaScript alerts, confirms and prompts without any special handling.
Scrolling Page
If certain elements are not visible in viewport, we need to scroll to them before any interaction:
button = driver.find_element(By.ID, ‘submit-button‘)
# scroll element into view
driver.execute_script("arguments[0].scrollIntoView();", button)
# alternative approach
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
actions.move_to_element(button);
actions.perform();
button.click()
Scrolling examples using plain JavaScript as well as Selenium‘s ActionChains class. You can choose what works better.
Implicit Waits
Often web applications take time to load content or render elements dynamically using JS.
Selenium provides flexible waits and sleeps to poll element readiness:
Implicit waits
driver.implicitly_wait(10)
This configures webdriver instance to retry lookups for duration of 10 seconds before throwing "ElementNotVisibleException". Great way to handle unpredictable delays.
Explicit waits
More advanced usecases may need intelligent waiting for element properties or conditions:
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
try:
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "myDynamicElement"))
)
except:
print("Element didn‘t appear in 10 seconds")
element.click()
Here we are explicitly waiting for element to become clickable with configurable timeout. Explicit waits give lot of flexibility to build waiting logic as per specific needs.
Headless Execution in Docker
Let‘s see how we can containerize tests to run in headless mode using Docker. This will simulate a CI/CD pipeline environment.
Dockerfile
FROM python:3.8-slim
RUN apt-get update && apt-get install -y firefox
RUN mkdir /app
COPY . /app/
WORKDIR /app
RUN pip install selenium
CMD ["python", "test_sample.py"]
Build the image:
$ docker build -t firefox-selenium .
Now run the container mapping host source code:
$ docker run -v "$(pwd)":/app firefox-selenium
This will queue test execution inside Docker with Firefox installed in headless mode. Very convenient!
For larger projects, Docker helps not only with headless execution but also brings in other benefits like:
- Test environment consistency
- Dependency management
- Faster onboarding
- Integration with CI tools
So take advantage of containers for taming test suites.
Common Issues
Let‘s go through some common pain points and fixes:
SSL errors
Sites with untrusted certificates may cause SSL handshake failures. To fix pass insecure cert argument:
capabilities = webdriver.DesiredCapabilities().FIREFOX
capabilities["acceptInsecureCerts"] = True
driver = webdriver.Firefox(capabilities=capabilities)
Browser log errors
Firefox logs all errors onto console output, making it difficult to find test failures. Add log suppression argument:
firefox_options = Options()
firefox_options.add_argument("-silent")
driver = webdriver.Firefox(options=firefox_options)
Import failures
If your scripts throw weird import errors, try upgrading selenium package fully:
$ pip install selenium --upgrade
Element not interactable
If clicking on elements throws error that element is not visible or interactable, try scrolling it into view before interacting:
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
actions.move_to_element(element).perform()
element.click()
This helps overcoming issues with elements down the page or hidden inside scrollable containers.
I hope these tips help with some frequent issues you may encounter.
And this sums up a good overview using Selenium bindings with Firefox. There are a lot more capabilities we didn‘t cover like downloading files, extensions, mobile simulation etc. But you should have all the basics now to start test automation with Selenium Python!


