Bird
Raised Fist0
PyTesttesting~15 mins

Given-When-Then pattern in PyTest - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - Given-When-Then pattern
What is it?
The Given-When-Then pattern is a way to write tests that clearly describe what is being tested. It breaks a test into three parts: Given (the starting situation), When (the action taken), and Then (the expected result). This helps anyone reading the test understand the purpose and flow easily.
Why it matters
Without this pattern, tests can be confusing and hard to follow, making it difficult to know what is being tested or why a test failed. Using Given-When-Then makes tests readable and maintainable, so teams can trust their tests and fix problems faster.
Where it fits
Before learning this, you should know basic testing concepts and how to write simple tests in pytest. After this, you can learn about Behavior-Driven Development (BDD) tools like pytest-bdd or Cucumber that build on this pattern.
Mental Model
Core Idea
Tests are stories told in three clear parts: setup (Given), action (When), and expected outcome (Then).
Think of it like...
It's like telling a friend how you baked a cake: first you explain the ingredients you have (Given), then what you did with them (When), and finally how the cake turned out (Then).
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│   GIVEN     │ → │    WHEN     │ → │    THEN     │
│ (Setup)    │   │ (Action)    │   │ (Outcome)   │
└─────────────┘   └─────────────┘   └─────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Test Structure Basics
🤔
Concept: Tests have parts: setup, action, and check.
In any test, you first prepare what you need (setup), then do something (action), and finally check if the result is what you expect (assertion). For example, to test adding numbers, you start with two numbers, add them, and check the sum.
Result
You see that tests follow a simple flow that matches how you think about problems.
Understanding that tests have these three parts helps you organize your thoughts and code clearly.
2
FoundationWriting Simple pytest Assertions
🤔
Concept: pytest uses assert statements to check expected results.
A pytest test function uses assert to verify outcomes. For example: def test_add(): result = 2 + 3 assert result == 5 This checks if adding 2 and 3 equals 5.
Result
The test passes if the assertion is true, fails otherwise.
Knowing how to write assertions is key to verifying behavior in tests.
3
IntermediateApplying Given-When-Then in pytest
🤔Before reading on: do you think Given-When-Then requires special pytest syntax or just a way to organize code? Commit to your answer.
Concept: Given-When-Then is a style to organize test code clearly, not a special pytest feature.
You write tests by dividing code into three parts with comments or blank lines: def test_multiply(): # Given x = 4 y = 5 # When result = x * y # Then assert result == 20 This makes the test easy to read and understand.
Result
Tests become more readable and maintainable without changing pytest itself.
Using Given-When-Then as a pattern improves communication in tests without needing extra tools.
4
IntermediateHandling Complex Setup in Given
🤔Before reading on: do you think all setup code should be inside the test function's Given section? Commit to your answer.
Concept: Setup can be extracted into fixtures to keep tests clean and focused.
pytest fixtures prepare complex setups outside the test function: import pytest @pytest.fixture def user(): return {'name': 'Alice', 'age': 30} def test_user_age(user): # Given # user fixture provides data # When age = user['age'] # Then assert age == 30 Fixtures keep Given clean and reusable.
Result
Tests stay simple and focused on action and outcome, while setup is reusable.
Separating setup into fixtures helps manage complexity and reuse in tests.
5
IntermediateUsing Given-When-Then for Behavior Clarity
🤔Before reading on: do you think Given-When-Then only helps with code structure or also with understanding test purpose? Commit to your answer.
Concept: The pattern clarifies not just code but the behavior being tested.
By labeling parts, tests tell a story: # Given a logged-in user # When they request their profile # Then they receive their data This helps anyone reading tests understand what behavior is expected, not just how code runs.
Result
Tests become documentation that explains system behavior clearly.
Using Given-When-Then turns tests into readable stories that communicate intent.
6
AdvancedIntegrating Given-When-Then with pytest-bdd
🤔Before reading on: do you think pytest-bdd requires rewriting tests or just adds syntax? Commit to your answer.
Concept: pytest-bdd adds syntax to write Given-When-Then in natural language linked to code.
pytest-bdd lets you write tests like: Feature: Calculator Scenario: Add numbers Given I have numbers 2 and 3 When I add them Then the result is 5 You link these steps to Python functions with decorators. This bridges human language and code.
Result
Tests become easier for non-programmers to read and collaborate on.
Using pytest-bdd connects Given-When-Then to BDD practices, improving team communication.
7
ExpertAvoiding Common Pitfalls in Given-When-Then Tests
🤔Before reading on: do you think mixing multiple actions in When is good practice? Commit to your answer.
Concept: Tests should keep When focused on a single action to avoid confusion and brittle tests.
Bad example: # When result1 = add(2, 3) result2 = multiply(4, 5) # Then assert result1 == 5 assert result2 == 20 Better to split into separate tests or steps. This keeps tests clear and failures easy to diagnose.
Result
Tests are more reliable and easier to maintain.
Keeping When focused prevents tests from becoming complex and hard to debug.
Under the Hood
Given-When-Then is a human-readable pattern that structures test code into three logical parts. pytest itself runs test functions and evaluates assertions. The pattern does not change pytest's execution but guides how developers write and organize tests for clarity and maintainability.
Why designed this way?
The pattern comes from Behavior-Driven Development (BDD) to bridge communication between technical and non-technical team members. It was designed to make tests readable like stories, improving collaboration and reducing misunderstandings about requirements.
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│   GIVEN     │ → │    WHEN     │ → │    THEN     │
│ (Setup)    │   │ (Action)    │   │ (Outcome)   │
└─────────────┘   └─────────────┘   └─────────────┘
       │               │               │
       ▼               ▼               ▼
  Prepare data    Execute code    Assert results
  or state       or behavior     to verify test
Myth Busters - 4 Common Misconceptions
Quick: Does Given-When-Then require special pytest commands to work? Commit to yes or no.
Common Belief:Given-When-Then is a special pytest feature or command that must be used to write tests.
Tap to reveal reality
Reality:Given-When-Then is a pattern or style for organizing tests, not a pytest command or feature.
Why it matters:Believing this can confuse beginners and make them think they need extra tools or syntax to write good tests.
Quick: Should the When section contain multiple actions or just one? Commit to your answer.
Common Belief:It's fine to put many actions in the When part to test complex flows in one test.
Tap to reveal reality
Reality:When should focus on a single action to keep tests clear and failures easy to diagnose.
Why it matters:Mixing many actions makes tests brittle and hard to understand or fix when they fail.
Quick: Is it okay to skip the Given part if setup is simple? Commit to yes or no.
Common Belief:If setup is simple, you can skip the Given part and just write When and Then.
Tap to reveal reality
Reality:Even simple setup should be clearly marked as Given to keep tests consistent and readable.
Why it matters:Skipping Given reduces clarity and makes tests harder to maintain as they grow.
Quick: Does Given-When-Then guarantee that tests cover all edge cases? Commit to yes or no.
Common Belief:Using Given-When-Then ensures tests are complete and cover all scenarios.
Tap to reveal reality
Reality:The pattern helps organize tests but does not guarantee coverage; test design still matters.
Why it matters:Relying on the pattern alone can lead to false confidence and missed bugs.
Expert Zone
1
Understanding that Given-When-Then is a communication tool as much as a coding pattern helps avoid over-engineering tests.
2
Recognizing that fixtures and parameterization complement Given-When-Then to handle complex setups and multiple scenarios elegantly.
3
Knowing that mixing Given-When-Then with other testing styles can confuse readers, so consistency in a project is key.
When NOT to use
Given-When-Then is less useful for very low-level unit tests that test tiny functions with no setup or behavior flow. In such cases, simple assert statements without the pattern are clearer and faster.
Production Patterns
In real projects, Given-When-Then is often combined with pytest fixtures for setup and pytest parameterize for multiple inputs. Teams use it to write acceptance tests and integrate with BDD tools like pytest-bdd to involve non-developers in test writing.
Connections
Behavior-Driven Development (BDD)
Given-When-Then is the core pattern used in BDD to describe behaviors.
Knowing Given-When-Then helps understand how BDD bridges communication between developers and business stakeholders.
Storytelling in Communication
Given-When-Then structures tests like a story with a beginning, middle, and end.
Understanding storytelling principles improves writing clear, engaging tests that explain behavior naturally.
Scientific Method
Given-When-Then mirrors the scientific method: setup conditions, perform an experiment, observe results.
Seeing tests as experiments helps design better tests and interpret failures as hypotheses to investigate.
Common Pitfalls
#1Mixing multiple actions in the When section.
Wrong approach:def test_actions(): # Given x = 2 y = 3 # When result1 = x + y result2 = x * y # Then assert result1 == 5 assert result2 == 6
Correct approach:def test_addition(): # Given x = 2 y = 3 # When result = x + y # Then assert result == 5 def test_multiplication(): # Given x = 2 y = 3 # When result = x * y # Then assert result == 6
Root cause:Trying to test multiple behaviors in one test makes When unclear and failures hard to diagnose.
#2Skipping the Given section when setup is simple.
Wrong approach:def test_simple(): result = 1 + 1 assert result == 2
Correct approach:def test_simple(): # Given a = 1 b = 1 # When result = a + b # Then assert result == 2
Root cause:Assuming simple setup does not need explanation reduces test clarity and consistency.
#3Using Given-When-Then as a strict syntax instead of a flexible pattern.
Wrong approach:def test(): Given = 'setup' When = 'action' Then = 'assertion' # code follows these variables
Correct approach:def test(): # Given setup code # When action code # Then assertion code
Root cause:Misunderstanding the pattern as code syntax rather than a way to organize tests.
Key Takeaways
Given-When-Then is a simple pattern that breaks tests into setup, action, and expected outcome to improve clarity.
This pattern is not a pytest feature but a style that makes tests easier to read and maintain.
Using clear sections helps communicate test purpose and behavior to all team members, including non-developers.
Separating setup into fixtures and keeping When focused on one action makes tests more reliable and reusable.
Given-When-Then connects testing to broader ideas like storytelling and the scientific method, enriching how we think about tests.

Practice

(1/5)
1. What is the main purpose of the Given-When-Then pattern in pytest tests?
easy
A. To write tests only for user interface elements
B. To organize tests into setup, action, and verification steps
C. To speed up test execution by skipping setup
D. To avoid using assertions in tests

Solution

  1. Step 1: Understand the pattern roles

    Given-When-Then divides a test into three parts: Given (setup), When (action), Then (check).
  2. Step 2: Match purpose with options

    Only To organize tests into setup, action, and verification steps correctly describes this organization purpose.
  3. Final Answer:

    To organize tests into setup, action, and verification steps -> Option B
  4. Quick Check:

    Given-When-Then = organize test steps [OK]
Hint: Remember: Given=setup, When=action, Then=check [OK]
Common Mistakes:
  • Thinking it only applies to UI tests
  • Believing it skips setup steps
  • Assuming no assertions are used
2. Which of the following is the correct way to write a Given-When-Then style pytest test function?
easy
A. def test_example(): # Given x = 5 # When y = x + 3 # Then assert y == 8
B. def test_example(): x = 5 y = x + 3 assert y == 8 # Given-When-Then comments missing
C. def test_example(): assert 5 + 3 == 8 # Given-When-Then not used
D. def test_example(): # When x = 5 # Given y = x + 3 # Then assert y == 8

Solution

  1. Step 1: Check comment order and code logic

    def test_example(): # Given x = 5 # When y = x + 3 # Then assert y == 8 correctly uses Given-When-Then comments in order with proper setup, action, and assertion.
  2. Step 2: Identify incorrect patterns

    The other options either lack Given-When-Then comments or have them in the wrong order (e.g., When before Given).
  3. Final Answer:

    def test_example():\n # Given\n x = 5\n # When\n y = x + 3\n # Then\n assert y == 8 -> Option A
  4. Quick Check:

    Correct comment order = def test_example(): # Given x = 5 # When y = x + 3 # Then assert y == 8 [OK]
Hint: Follow comment order: Given, When, Then [OK]
Common Mistakes:
  • Mixing the order of Given and When comments
  • Skipping comments entirely
  • Placing assertions outside Then step
3. Given the following pytest test using Given-When-Then pattern, what will be the test result?
def test_sum():
    # Given
    numbers = [1, 2, 3]
    # When
    total = sum(numbers)
    # Then
    assert total == 6
medium
A. Test will fail because assertion is wrong
B. Test will fail because sum is calculated incorrectly
C. Test will error due to missing import
D. Test will pass because sum of [1, 2, 3] is 6

Solution

  1. Step 1: Calculate sum of list

    The sum of [1, 2, 3] is 1+2+3 = 6.
  2. Step 2: Check assertion correctness

    The assertion checks total == 6, which is true, so test passes.
  3. Final Answer:

    Test will pass because sum of [1, 2, 3] is 6 -> Option D
  4. Quick Check:

    sum([1,2,3]) = 6 [OK]
Hint: Calculate sum and compare with assertion value [OK]
Common Mistakes:
  • Assuming sum returns a list instead of number
  • Thinking assertion expects a different value
  • Confusing test error with failure
4. Identify the error in this Given-When-Then pytest test:
def test_multiply():
    # Given
    x = 4
    y = 5
    # When
    result = x * y
    # Then
    assert result = 20
medium
A. Syntax error in assertion statement
B. Wrong variable names used
C. Missing setup in Given step
D. No assertion in Then step

Solution

  1. Step 1: Check assertion syntax

    The assertion uses single '=' which is assignment, not comparison. It should be '==' for comparison.
  2. Step 2: Confirm other parts are correct

    Variables and steps are correct; only assertion syntax is wrong.
  3. Final Answer:

    Syntax error in assertion statement -> Option A
  4. Quick Check:

    Use '==' in assert, not '=' [OK]
Hint: Use '==' for assert comparisons, not '=' [OK]
Common Mistakes:
  • Using '=' instead of '==' in assert
  • Confusing assignment with comparison
  • Ignoring syntax errors in assertions
5. You want to test a function that filters out falsy values (like 0, '', None) from a list using Given-When-Then pattern in pytest. Which test code correctly applies this pattern and checks the result?
hard
A. def test_filter_falsy(): # Given data = [0, 1, '', 'hello', None, True] # Then assert filtered == [1, 'hello', True] # When filtered = [x for x in data if x]
B. def test_filter_falsy(): data = [0, 1, '', 'hello', None, True] filtered = [x for x in data if x] assert filtered == [1, 'hello', True]
C. def test_filter_falsy(): # Given data = [0, 1, '', 'hello', None, True] # When filtered = [x for x in data if x] # Then assert filtered == [1, 'hello', True]
D. def test_filter_falsy(): # Given data = [0, 1, '', 'hello', None, True] # When filtered = filter(lambda x: x, data) # Then assert list(filtered) == [0, 1, '', 'hello', None, True]

Solution

  1. Step 1: Verify Given-When-Then structure

    def test_filter_falsy(): # Given data = [0, 1, '', 'hello', None, True] # When filtered = [x for x in data if x] # Then assert filtered == [1, 'hello', True] correctly uses Given for data setup, When for filtering action, Then for assertion check.
  2. Step 2: Check correctness of filtering and assertion

    Filtering removes falsy values; expected list matches filtered result. Other options miss comments or have wrong assertion.
  3. Final Answer:

    def test_filter_falsy():\n # Given\n data = [0, 1, '', 'hello', None, True]\n # When\n filtered = [x for x in data if x]\n # Then\n assert filtered == [1, 'hello', True] -> Option C
  4. Quick Check:

    Given-When-Then + correct filter = def test_filter_falsy(): # Given data = [0, 1, '', 'hello', None, True] # When filtered = [x for x in data if x] # Then assert filtered == [1, 'hello', True] [OK]
Hint: Keep steps clear: setup, action, then check [OK]
Common Mistakes:
  • Skipping Given-When-Then comments
  • Asserting wrong filtered list
  • Using filter object without converting to list