Behavior Driven Development (BDD) with Selenium in Python

A beginner guide

Gerald Nguyen
Gerald Nguyen
8 min read ·
Previous | Next
Also on Medium
On this page

This is a continuation from my previous article where we learned the basic of Selenium and how to program it in Python to perform UI automation testing.

Getting started on UI automation testing using Selenium with Python

Five Minimum Viable Interview Questions series — Updated 17 Feb 2023
Five Minimum Viable Interview Questions series — Updated 17 Feb 2023

Interviewer: ask them if your interview is running out of time. Interviewee: practice them if your preparation is running out of time.

Basics of BDD

Behavior Driven Development is a popular software development discipline. As its name suggests, BDD project is guided by definition and verification of behaviors. A common form of behavior can be expressed in the form of

given <some pre-requisites>

when <some actions happen>

then <some results to verify>

This form of expression is popular enough that people created a language called Gherkin to express and extend it. Gherkin organizes a software’s behaviors into one or more .feature files, each may contain one or more scenarios in the given…when…then form.

Example:

# Source: https://cucumber.io/docs/gherkin/reference/#example  
Feature: Guess the word  
  
  # The first example has two steps  
  Scenario: Maker starts a game  
    When the Maker starts a game  
    Then the Maker waits for a Breaker to join  
  
  # The second example has three steps  
  Scenario: Breaker joins a game  
    Given the Maker has started a game with the word "silky"  
    When the Breaker joins the Maker's game  
    Then the Breaker must guess a word with 5 characters
Gherkin as a language?

Gherkin as a language?

BDD in Software Development and Testing

Software development team uses BDD to describe software behaviors in simple, natural and consistent language. Its main functions are thus a documentation and communication tool across all disciplines and stakeholders such as end users, product managers, developers, testers.

Arguably, BDD is often considered a special form of Test Driven Development (TDD) and is most popular among software testers or developers who develop automation tests.

Implementing the Behavior in BDD

Each BDD expression has a corresponding implementation. Take the second scenario above for example:

When a scenario read Given the Maker has started a game with the word “silky”, there are setup codes executing to create a new gameboard with the specified word “silky”

@given('the Maker has started a game with the word "silky"')  
def step_impl(context):  
    context.game = Game("silky")

The implementation can be very elaborated or can be just as simple as doing nothing

@when(u'the Breaker joins the Maker\'s game')  
def step_impl(context):  
    pass

It’s an error if we miss an implementation. Suppose we forgot to implement the last expression Then the Breaker must guess a word with 5 characters , we will receive the following error:

Missing implementation error

Missing implementation error

BDD with Selenium in Python: Overview

Dependencies

In Python, we can use Behave to run BDD tests. It will pick up the .features file, their implementation and execute them accordingly.

pip install behave

We also need Selenium to launch and control Chrome

pip install selenium

Organization

We will organize our files in the following structure:

Root project folder    
  |-- features  
        |-- steps  
        |    |-- a_feature_implementation.py  
        |-- environment.py  
        |-- a_feature.feature

Write Feature files and implement the steps

See the demo in the next section

Execute the test

See the demo in the next section

BDD with Selenium in Python: Demo

We will demonstrate how to describe and implement the 2 tests in the previous article in BDD style.

Quick Recap

import unittest  
from selenium import webdriver  
from selenium.webdriver.common.by import By  
  
class TestPythonOrg(unittest.TestCase):  
    def setUp(self):  
        self.driver = webdriver.Chrome()  
  
    def tearDown(self):  
        self.driver.quit()  
  
    def test_tc1_get_python_org_title(self):  
        driver = self.driver  
        driver.get("https://www.python.org")  
  
        print(driver.title)  
        self.assertEqual("Welcome to Python.org", driver.title)  
  
  
    def test_tc2_search_getting_started(self):  
        driver = self.driver  
  
        driver.get("https://www.python.org")  
  
        search_input_field = driver.find_element(By.ID, "id-search-field")  
        search_input_field.clear()  
        search_input_field.send_keys("getting started in Python")  
  
        search_go_button = driver.find_element(By.ID, "submit")  
        search_go_button.click()  
  
        print("current url: " + driver.current_url)  
        self.assertEqual("https://www.python.org/search/?q=getting+started+in+Python&submit=", driver.current_url)  
  
        results = driver.find_elements(By.CSS_SELECTOR, ".list-recent-events li")  
        print(f"number of results: {len(results)}")  
        self.assertGreater(len(results), 0)  
  
if __name__ == '__main__':  
    unittest.main()

BDD Scenarios

We can express the previous test cases as the following 2 scenarios. We place them under a single feature here but you can have them in separate .feature files.

# file: lauch_python_org.feature  
  
Feature: Launch and Search Python.org  
  
    Scenario: Launch Python.org  
      Given we have selenium installed  
       When we launch https://www.python.org in chrome  
       Then we obtain title "Welcome to Python.org"  
  
    Scenario: Search Getting started in Python  
      Given we have selenium installed  
       When we launch https://www.python.org in chrome  
        And we search "getting started in Python"  
       Then we arrive at search page  
        And we obtain some result

In both scenario:

We can observe that And has flexible meanings depending on where it is used. It takes after the keyword it follows:

Implementing the behaviors

Since our 2 test scenarios requires the use of Selenium to launch and control Chrome browser — so-called test’s fixture, we’ll create an environment.py to set that up.

# file: features/environment.py  
from behave import fixture, use_fixture  
from selenium import webdriver  
  
@fixture  
def selenium_browser_chrome(context):  
    context.browser = webdriver.Chrome()  
    yield context.browser  
    context.browser.quit()  
  
def before_all(context):  
    use_fixture(selenium_browser_chrome, context)

We implement the remaining behaviors as below:

# file: features/steps/lauch_python_org.py  
  
from behave import *  
from selenium.webdriver.common.by import By  
  
@given('we have selenium installed')  
def step_impl(context):  
    assert hasattr(context, 'browser')  
  
@when('we launch https://www.python.org in chrome')  
def step_impl(context):  
    context.browser.get("https://www.python.org")  
  
@then('we obtain title "Welcome to Python.org"')  
def step_impl(context):  
    assert context.browser.title == "Welcome to Python.org"  
  
@when('we search "getting started in Python"')  
def step_impl(contex):  
    browser = contex.browser  
    search_input_field = browser.find_element(By.ID, "id-search-field")  
    search_input_field.clear()  
    search_input_field.send_keys("getting started in Python")  
  
    search_go_button = browser.find_element(By.ID, "submit")  
    search_go_button.click()  
  
@then('we arrive at search page')  
def step_impl(context):  
    assert context.browser.current_url == "https://www.python.org/search/?q=getting+started+in+Python&submit="  
  
@then('we obtain some result')  
def step_impl(context):  
    results = context.browser.find_elements(By.CSS_SELECTOR, ".list-recent-events li")  
    assert  len(results) > 0

Execute the test

Depending on how you setup your project, it can be as simple as typing behave into the terminal prompt (in PyCharm virtual environment setup), or as complicated as locating where behave is installed in order to execute it.

If you have run pip install behave previously, you can also type python -m behave to execute the test

PS ...\bdd-getting-started> behave  
DevTools listening on ws://127.0.0.1:59978/devtools/browser/9ab3805c-031d-45cc-bd58-b601e1979e50  
Feature: Launch and Search Python.org # features/lauch_python_org.feature:1  
  Scenario: Launch Python.org                       # features/lauch_python_org.feature:3  
    Given we have selenium installed                # features/steps/launch_python_org.py:5  
    When we launch https://www.python.org in chrome # features/steps/launch_python_org.py:8  
    Then we obtain title "Welcome to Python.org"    # features/steps/launch_python_org.py:12  
  Scenario: Search Getting started in Python        # features/lauch_python_org.feature:8  
    Given we have selenium installed                # features/steps/launch_python_org.py:5  
    When we launch https://www.python.org in chrome # features/steps/launch_python_org.py:8  
    And we search "getting started in Python"       # features/steps/launch_python_org.py:16  
    Then we arrive at search page                   # features/steps/launch_python_org.py:26  
    And we obtain some result                       # features/steps/launch_python_org.py:30  
1 feature passed, 0 failed, 0 skipped  
2 scenarios passed, 0 failed, 0 skipped  
8 steps passed, 0 failed, 0 skipped, 0 undefined  
Took 0m1.844s

Conclusion

We have briefly learn what Behavior-Driven development is, how a software development team uses it, and how we can do so with Behave, Selenium in Python.

For the demo, we converted our previous project, which contains 2 simple Selenium-based UI automation tests, into 2 BDD scenarios. You can get the sample codes from the GitHub link below

GitHub - geraldnguyen/bdd-getting-started: Behavior Driven Development (BDD) with Selenium in…