A Recipe for Website Automated Tests with Python Selenium & Headless Chrome in Docker

As a full-stack developer, you know the importance of testing your web applications to catch bugs early and ensure a smooth user experience. Manual testing is time-consuming and error-prone, especially as your codebase grows. Automating your tests is crucial for maintaining quality at scale.

In this guide, I‘ll show you how to use industry-standard open source tools to set up automated tests for your website:

  • Python – the dynamic programming language loved by web developers for its simplicity and rich ecosystem
  • Selenium WebDriver – the go-to library for automating browsers
  • Chrome – the popular web browser (run in headless mode for faster testing)
  • Docker – the container platform for packaging your test environment for reproducible results

By the end, you‘ll have a working example test case and understand the key steps to write your own effective test suite. Let‘s get started!

Setting Up the Test Environment

Our first step is to launch a pre-configured Docker container with all the dependencies we need to run Selenium tests in Python. Using a container ensures everyone on your team has a consistent environment and avoids "works on my machine" issues.

We‘ll use this public image: joyzoursky/python-chromedriver. It comes with Python 3.6, Selenium, and headless Chrome pre-installed.

Open your terminal and navigate to your working directory, then run this command to pull the image and start the container:


$ docker run -it --rm \
  -v $(pwd):/usr/workspace \
  joyzoursky/python-chromedriver:3.6-alpine3.7-selenium \
  sh

This will drop you into a shell session inside the running container. The -v flag mounts your current working directory to /usr/workspace inside the container, so you can easily pass files between your local environment and the containerized one.

Let‘s move to the workspace and launch Python:


/# cd /usr/workspace/
/usr/workspace # python
Python 3.6.4 (default, Jan 10 2018, 05:20:21)
[GCC 6.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Configuring Selenium

Before we can automate the browser, we need to import the Selenium WebDriver library and set some configuration options for Chrome.


>>> from selenium import webdriver
>>>
>>> chrome_options = webdriver.ChromeOptions()
>>> chrome_options.add_argument(‘--no-sandbox‘)
>>> chrome_options.add_argument(‘--headless‘)
>>> chrome_options.add_argument(‘--disable-gpu‘)
>>> driver = webdriver.Chrome(chrome_options=chrome_options)

The --no-sandbox flag is necessary to run Chrome inside the container. --headless runs Chrome in headless mode, without a visible UI. This makes the tests faster and avoids unnecessary resource consumption since we don‘t need to actually see the browser window.

--disable-gpu further optimizes Chrome for the headless environment.

Writing a Test Case

With the setup out of the way, we‘re ready to write our first test! Let‘s start with something simple: navigating to a site, locating a specific element, clicking a button, and verifying we ended up on the expected page.


>>> driver.get(‘https://www.example.com‘)
>>> el = driver.find_element_by_css_selector(‘#signup-button‘)
>>> el.click()
>>> assert ‘https://www.example.com/signup‘ in driver.current_url

Here‘s what each line does:

  1. Navigate to the initial URL with driver.get()
  2. Locate the button element using its ID attribute with find_element_by_css_selector()
  3. Simulate clicking the button by calling el.click()
  4. Check that the resulting page URL contains the expected path using assert

If all goes well, you‘ve just run your first Selenium test in Python! The test will pass if the assertion holds true, meaning clicking the #signup-button took us to a URL ending in /signup.

Best Practices for Effective Test Cases

One test is a good start, but a comprehensive test suite requires strategic planning. Here are some tips for writing robust and maintainable tests:

Use Page Objects
The Page Object Model is a common pattern that reduces duplication by representing each page in your app as its own class, with locators and interaction methods specific to that page. Whenever you need to make a change, you only have to update the page object instead of dozens of individual test cases.

Make Assertions Specific
It‘s important to verify the expected outcome of each test case, but overly general assertions like assert True don‘t provide much value. Make your assertions as precise as possible while still accounting for content that may change frequently.

Use Explicit Waits
Web apps are asynchronous by nature and elements may not be immediately interactable when a page loads. Selenium‘s WebDriverWait lets you wait for specific elements to be present or visible before interacting with them, making your tests less brittle.

Keep Tests Independent
Each test should set up and tear down its own environment, without relying on global state. This avoids cascading failures and makes it easier to run tests in parallel. Use Selenium‘s setUp() and tearDown() methods to handle the test lifecycle.

Parametrize Your Tests
Don‘t copy-paste the same test with different input values – use your test runner‘s parametrization features to generate multiple tests from a single set of parameters. This is cleaner and makes it easy to expand your test coverage.

Continuous Integration

To get the most value out of your automated tests, they should run on every change to your application‘s codebase. This is known as continuous integration (CI).

Most CI tools support running containerized tests, so you can use the same Docker image from this guide in your CI pipeline. Here‘s an example configuration for the popular GitLab CI:

stages: 
  - test

test: stage: test image: joyzoursky/python-chromedriver:3.6-alpine3.7-selenium script:

  • python -m unittest discover tests/

This simple pipeline has a single stage that runs all Python files in the tests/ directory using the standard unittest module. You could also use a more powerful test runner like pytest.

Next Steps

In this guide, you‘ve learned how to:

  • Set up a Dockerized environment for Selenium tests in Python
  • Configure Selenium to run Chrome in headless mode
  • Write a basic test case using Selenium WebDriver methods
  • Structure an effective test suite
  • Integrate your tests with a CI pipeline

This only scratches the surface of what‘s possible with Selenium. To take your automated testing to the next level, dive deeper into more advanced WebDriver interactions, like working with complex page elements, filling out forms, and handling JavaScript behavior.

You can also explore tools to help you write tests more efficiently, like the Selenide and Capybara libraries that provide a higher-level, more expressive API on top of Selenium.

With a solid foundation in web app testing and an understanding of the available tools and best practices, you‘ll be able to ensure your applications are high-quality and bug-free. Happy testing!

Similar Posts