Sam Pegler

Tech Leadership

Worked at BritX1 and Ki2 prior to moving back to Brit, experience in Python and SRE.

Here to talk about Behvaiour Driven Development (BDD) and how it can help us.

What is TDD?

How does BDD3 differ from TDD?

So what is the BDD core loop?

BDD is about building the right thing, not really about how it’s built.

Does that mean you don’t have to use TDD?

What do behavioural tests look like?

Feature: Google Searching

  Scenario: Search from the search bar
    Given a web browser is at the Google home page
    When the user enters "panda" into the search bar
    Then links related to "panda" are shown on the results page

Expanding behaviours should be done by adding more scenarios, not increasing the footprint of the test.

Feature: Google Searching

  Scenario: Search from the search bar
    Given a web browser is at the Google home page
    When the user enters "panda" into the search bar
    Then links related to "panda" are shown on the results page

  Scenario: Image search
    Given Google search results for "panda" are shown
    When the user clicks on the "Images" link at the top of the results page
    Then images related to "panda" are shown on the results page

BDD aligns development and product with a contract, the feature file.

Behavioural scenarios can be shared with not just product, but almost any stakeholder.

What toolchain is best to use in Python?

behave or pytest-bdd

behave is full behavioural testing framework AND runner.

Feature: Showing off behave

  Scenario: Run a simple test
    Given we have behave installed
     When we implement 5 tests
     Then behave will test them for us!
from behave import given, when, then, step

@given('we have behave installed')
def step_impl(context):
    pass

@when('we implement {number:d} tests')
def step_impl(context, number):
    assert number > 1 or number == 0
    context.tests_count = number

@then('behave will test them for us!')
def step_impl(context):
    assert context.failed is False
    assert context.tests_count >= 0
% behave
Feature: Showing off behave

  Scenario: Run a simple test
    Given we have behave installed
    When we implement 5 tests
    Then behave will test them for us!

1 feature passed, 0 failed, 0 skipped
1 scenario passed, 0 failed, 0 skipped
3 steps passed, 0 failed, 0 skipped, 0 undefined

Reporting tools can ingest the json reporting into azure pipelines.

pytest-bdd is a plugin for pytest that includes fixtures for running behavioural tests

Feature: Showing off pytest-bdd

  Scenario: Run a simple test
    Given we have pytest-bdd installed
     When we implement 5 tests
     Then pytest will test them for us!
from pytest_bdd import scenarios, parsers, given, when, then

scenarios("pytest-bdd.scenario")

@given('we have pytest-bdd installed')
def pytest_bdd_is_installed():
    pass

@when(
    parsers.parse("we implement {number:d} tests"), target_fixture="number_of_tests"
)
def number_of_tests(number: int):
    assert number > 1 or number == 0
    return number

@then('pytest-bdd will test them for us!')
def step_impl(number_of_tests: int):
    assert number_of_tests >= 0
% poetry run pytest -vv --gherkin-terminal-reporter tests
========================================== test session starts ==========================================
platform darwin -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 -- /Users/sampegler/Library/Caches/pypoetry/virtualenvs/test-8DRKBHXr-py3.13/bin/python
cachedir: .pytest_cache
rootdir: /private/tmp/test
configfile: pyproject.toml
plugins: bdd-7.3.0
collected 1 item

tests/test_bdd.py::test_run_a_simple_test <- ../../../Users/sampegler/Library/Caches/pypoetry/virtualenvs/test-8DRKBHXr-py3.13/lib/python3.13/site-packages/pytest_bdd/scenario.py
Feature: Showing off pytest-bdd
    Scenario: Run a simple test
        Given we have pytest-bdd installed
        When we implement 5 tests
        Then pytest-bdd will test them for us!
    PASSED


=========================================== 1 passed in 0.00s ===========================================

The same reporting tools used for behave can also be used here.

Marks can be placed directly in scenario files and then picked up by pytest.

@bdd
Feature: Google Searching
	
  @search
  Scenario: Search from the search bar
    Given a web browser is at the Google home page
    When the user enters "panda" into the search bar
    Then links related to "panda" are shown on the results page

  @search @images
  Scenario: Image search
    Given Google search results for "panda" are shown
    When the user clicks on the "Images" link at the top of the results page
    Then images related to "panda" are shown on the results page

pytest -m 'bdd and images' would only run the second scenario.

When it comes to making a choice pytest-bdd has already been used in Brit so is heavily recommended.

Paramaterisation

Feature: Scenario outlines
  Scenario Outline: Outlined given, when, then
    Given there are <start> cucumbers
    When I eat <eat> cucumbers
    Then I should have <left> cucumbers

    Examples:
    | start | eat | left |
    |  12   |  5  |  7   |
    |  2    |  2  |  0   |

Like pytest.mark.parametrize in pytest, we can paramaterise with pytest-bdd. The Scenario Outline keyword explains to the parser that this scenario is to be run multiple times with the example data.

Populating data

Scenario: Correct non-zero number of books found by author
  Given I have the following books in the store
    | title                                | author      |
    | The Devil in the White City          | Erik Larson |
    | The Lion, the Witch and the Wardrobe | C.S. Lewis  |
    | In the Garden of Beasts              | Erik Larson |
  When I search for books by author Erik Larson
  Then I find 2 books

Data tables4 are passed inserted into a pytest fixture named datatable and can be accessed as any other fixture. This could be then used in a @given decorated step to pre-populate the DB.

When to use BDD?

When not to use BDD?

Real world examples

Scenario: Ki Cargo only Brit new business policy 100% in the EU
  Given a completed PBQA
  And the syndicate '1618' is on risk
  And the number of references is '1'
  And the 'policy_reference' field is 'GP705S21A000'
  And the 'journey' field is 'BRIT_NEW_BUSINESS'
  And the 'class_of_business' field is 'CARGO'
  And the 'eea' field is '100'
  When I generate a reference
  Then it should complete successfully
  And the number of references should be '1'
  And the references for syndicate '1618' should be ['GP705S21A000']

Follow on tutorials

  1. Writing good gherkin is a pretty essential read for writing effective behavioural tests.
  2. pytest-with-eric does a great worked introduction.
  1. Worked on a tool called Otto which was used in Binding and Endorsements for Ki. Some of this work ended up as the Sovreign project at Brit. ↩︎

  2. Worked on portfolio management and observability & monitoring. ↩︎

  3. Specification by example is a good book on BDD. ↩︎

  4. You’ll want to use the latest pre release version of pytest-bdd for this, it should land on main in the next couple of weeks. ↩︎