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:

  1. Import Selenium bindings
  2. Specify path to GeckoDriver executable
  3. Create new Firefox webdriver instance
  4. Open LinuxHint homepage
  5. Print out page title
  6. 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!

Similar Posts