Tutorial 7: Result Table

Goal:Use result tables to simplify comparison of an expected dataset.

The usage of result tables come in variations. It often depends what you want to compare. These variations in the test automation layer are:

  • ordered dataset comparison
  • unordered dataset comparison
  • ordered subset comparison (result table contains subset)
  • unordered subset comparison (result table contains subset)

Hint

The FIT test framework provides similar concepts via Fixtures. An extension of FIT, the FitLibrary, provides even more advanced fixtures classes/tables.

Dataset Unordered comparison Ordered Comparison
Subset fitlibrary.SubsetFixture, fit.RowFixture (with table args) fitlibrary.ArrayFixture (variant)
Complete fit.RowFixture, fitlibrary.SetFixture fitlibrary.ArrayFixture

Besides other descriptions of these Fixtures, the Fixture Gallery project provides examples for these fixture in several languages.

Both, unordered dataset comparison and unordered subset comparison are used in this tutorial in two different scenarios.

Write the Feature Test

# file:features/tutorial07_step_result_table.feature
Feature: Step Result Table (tutorial07)

   Scenario: Unordered Result Table Comparison (RowFixture Table)
     Given a set of specific users:
        | name      | department  |
        | Alice     | Beer Cans   |
        | Bob       | Beer Cans   |
        | Charly    | Silly Walks |
        | Dodo      | Silly Walks |
    Then we will have the following people in "Silly Walks":
        | name    |
        | Charly  |
        | Dodo    |
    And we will have the following people in "Beer Cans":
        | name    |
        | Bob     |
        | Alice   |


   Scenario: Subset Result Table Comparison
     Given a set of specific users:
        | name      | department       |
        | Alice     | Super-sonic Cars |
        | Bob       | Super-sonic Cars |
    Then we will have at least the following people in "Super-sonic Cars":
        | name    |
        | Alice   |

Provide the Test Automation

# file:features/steps/step_tutorial07.py
# ----------------------------------------------------------------------------
# STEPS:
# ----------------------------------------------------------------------------
from behave   import given, when, then
from hamcrest import assert_that, has_items
from hamcrest.library.collection.issequence_containinginanyorder \
    import contains_inanyorder

@then('we will have the following people in "{department}"')
def step_impl(context, department):
    """
    Compares expected with actual persons in a department.
    NOTE: Unordered comparison (ordering is not important).
    """
    department_ = context.model.departments.get(department, None)
    if not department_:
        assert_that(False, "Department %s is unknown" % department)
    # -- NORMAl-CASE:
    expected_persons = [ row["name"]    for row in context.table ]
    actual_persons   = department_.members

    # -- UNORDERED TABLE-COMPARISON (using: pyhamcrest)
    assert_that(contains_inanyorder(*expected_persons), actual_persons)

@then('we will have at least the following people in "{department}"')
def step_impl(context, department):
    """
    Compares subset of persons with actual persons in a department.
    NOTE: Unordered subset comparison.
    """
    department_ = context.model.departments.get(department, None)
    if not department_:
        assert_that(False, "Department %s is unknown" % department)
        # -- NORMAl-CASE:
    expected_persons = [ row["name"]    for row in context.table ]
    actual_persons   = department_.members

    # -- TABLE-SUBSET-COMPARISON (using: pyhamcrest)
    assert_that(has_items(*expected_persons), actual_persons)

# @mark.more_steps
# ----------------------------------------------------------------------------
# MORE STEPS: step_tutorial06.py
# ----------------------------------------------------------------------------
# @given('a set of specific users')
# def step_impl(context):
#    model = getattr(context, "model", None)
#    if not model:
#        context.model = CompanyModel()
#    for row in context.table:
#        context.model.add_user(row["name"], deparment=row["department"])
#
# @when('we count the number of people in each department')
# def step_impl(context):
#    context.model.count_persons_per_department()

Provide the Domain Model

# file:features/steps/company_model.py
# -----------------------------------------------------------------------------
# DOMAIN-MODEL:
# -----------------------------------------------------------------------------
class Department(object):
    def __init__(self, name, members=None):
        if not members:
            members = []
        self.name = name
        self.members = members

    def add_member(self, name):
        assert name not in self.members
        self.members.append(name)

    @property
    def count(self):
        return len(self.members)

    def __len__(self):
        return self.count

class CompanyModel(object):
    def __init__(self):
        self.users = []
        self.departments = {}

    def add_user(self, name, deparment):
        assert name not in self.users
        if deparment not in self.departments:
            self.departments[deparment] = Department(deparment)
        self.departments[deparment].add_member(name)

    def count_persons_per_department(self):
        pass

    def get_headcount_for(self, department):
        return self.departments[department].count

Run the Feature Test

When you run the feature file from above:

$ behave ../features/tutorial07_step_result_table.feature
Feature: Step Result Table (tutorial07)   # ../features/tutorial07_step_result_table.feature:1

  Scenario: Unordered Result Table Comparison (RowFixture Table)  # ../features/tutorial07_step_result_table.feature:3
    Given a set of specific users                                 # ../features/steps/step_tutorial06.py:28
      | name   | department  |
      | Alice  | Beer Cans   |
      | Bob    | Beer Cans   |
      | Charly | Silly Walks |
      | Dodo   | Silly Walks |
    Then we will have the following people in "Silly Walks"       # ../features/steps/step_tutorial07.py:46
      | name   |
      | Charly |
      | Dodo   |
    And we will have the following people in "Beer Cans"          # ../features/steps/step_tutorial07.py:46
      | name  |
      | Bob   |
      | Alice |

  Scenario: Subset Result Table Comparison                                # ../features/tutorial07_step_result_table.feature:20
    Given a set of specific users                                         # ../features/steps/step_tutorial06.py:28
      | name  | department       |
      | Alice | Super-sonic Cars |
      | Bob   | Super-sonic Cars |
    Then we will have at least the following people in "Super-sonic Cars" # ../features/steps/step_tutorial07.py:62
      | name  |
      | Alice |

1 feature passed, 0 failed, 0 skipped
2 scenarios passed, 0 failed, 0 skipped
5 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.002s