Tutorial 10: User-defined Data Type¶
Goal: | Show how user-defined data types can be used in step parameters. |
---|
User-defined data types simplify the processing in step definitions. The string parameters are automatically parsed and converted into specific data types.
Note
Besides conversion into a user-defined type, this mechanism can also be used for text transformations that occurs before the parameter is handed to the step definition function.
Write the Feature Test¶
# file:features/tutorial10_step_usertype.feature
Feature: User-Defined Datatype as Step Parameter (tutorial10)
As a test writer
I want that a step parameter is converted into a specific datatype
to simplify the programming of the step definition body.
Scenario Outline: Calculator
Given I have a calculator
When I add "<x>" and "<y>"
Then the calculator returns "<sum>"
Examples:
| x | y | sum |
| 1 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 1 | 3 |
| 2 | 7 | 9 |
Provide the Test Automation¶
First you need to provide the type converter for Number
and register it:
# file:features/steps/step_tutorial10.py
# ----------------------------------------------------------------------------
# USER-DEFINED TYPES:
# ----------------------------------------------------------------------------
from behave import register_type
def parse_number(text):
"""
Convert parsed text into a number.
:param text: Parsed text, called by :py:meth:`parse.Parser.parse()`.
:return: Number instance (integer), created from parsed text.
"""
return int(text)
# -- REGISTER: User-defined type converter (parse_type).
register_type(Number=parse_number)
Now you can use Number
as type in step parameters for the step definitions:
# file:features/steps/step_tutorial10.py
# ----------------------------------------------------------------------------
# STEPS:
# ----------------------------------------------------------------------------
from behave import given, when, then
from hamcrest import assert_that, equal_to
from calculator import Calculator
@given('I have a calculator')
def step_impl(context):
context.calculator = Calculator()
@when('I add "{x:Number}" and "{y:Number}"')
def step_impl(context, x, y):
assert isinstance(x, int)
assert isinstance(y, int)
context.calculator.add2(x, y)
@then('the calculator returns "{expected:Number}"')
def step_impl(context, expected):
assert isinstance(expected, int)
assert_that(context.calculator.result, equal_to(expected))
Provide the Domain Model¶
# file:features/steps/calculator.py
# -----------------------------------------------------------------------------
# DOMAIN-MODEL:
# -----------------------------------------------------------------------------
class Calculator(object):
def __init__(self, value=0):
self.result = value
def reset(self):
self.result = 0
def add2(self, x, y):
self.result += (x + y)
return self.result
Run the Feature Test¶
When you run the feature file from above:
$ behave ../features/tutorial10_step_usertype.feature Feature: User-Defined Datatype as Step Parameter (tutorial10) # ../features/tutorial10_step_usertype.feature:1 As a test writer I want that a step parameter is converted into a specific datatype to simplify the programming of the step definition body. Scenario Outline: Calculator -- @1.1 # ../features/tutorial10_step_usertype.feature:14 Given I have a calculator # ../features/steps/step_tutorial10.py:44 When I add "1" and "1" # ../features/steps/step_tutorial10.py:48 Then the calculator returns "2" # ../features/steps/step_tutorial10.py:54 Scenario Outline: Calculator -- @1.2 # ../features/tutorial10_step_usertype.feature:15 Given I have a calculator # ../features/steps/step_tutorial10.py:44 When I add "1" and "2" # ../features/steps/step_tutorial10.py:48 Then the calculator returns "3" # ../features/steps/step_tutorial10.py:54 Scenario Outline: Calculator -- @1.3 # ../features/tutorial10_step_usertype.feature:16 Given I have a calculator # ../features/steps/step_tutorial10.py:44 When I add "2" and "1" # ../features/steps/step_tutorial10.py:48 Then the calculator returns "3" # ../features/steps/step_tutorial10.py:54 Scenario Outline: Calculator -- @1.4 # ../features/tutorial10_step_usertype.feature:17 Given I have a calculator # ../features/steps/step_tutorial10.py:44 When I add "2" and "7" # ../features/steps/step_tutorial10.py:48 Then the calculator returns "9" # ../features/steps/step_tutorial10.py:54 1 feature passed, 0 failed, 0 skipped 4 scenarios passed, 0 failed, 0 skipped 12 steps passed, 0 failed, 0 skipped, 0 undefined Took 0m0.003s
The Complete Picture¶
# file:features/steps/step_tutorial10.py
# -*- coding: UTF-8 -*-
"""
Based on ``behave tutorial``
Feature: A Step uses a User-Defined Type as Step Parameter (tutorial10)
Scenario Outline: Calculator
Given I have a calculator
When I add "<x>" and "<y>"
Then the calculator returns "<sum>"
Examples: Add Numbers
| x | y | sum |
| 1 | 1 | 2 |
| 1 | 2 | 3 |
| 2 | 1 | 3 |
| 2 | 7 | 9 |
"""
# @mark.user_defined_types
# ----------------------------------------------------------------------------
# USER-DEFINED TYPES:
# ----------------------------------------------------------------------------
from behave import register_type
def parse_number(text):
"""
Convert parsed text into a number.
:param text: Parsed text, called by :py:meth:`parse.Parser.parse()`.
:return: Number instance (integer), created from parsed text.
"""
return int(text)
# -- REGISTER: User-defined type converter (parse_type).
register_type(Number=parse_number)
# @mark.steps
# ----------------------------------------------------------------------------
# STEPS:
# ----------------------------------------------------------------------------
from behave import given, when, then
from hamcrest import assert_that, equal_to
from calculator import Calculator
@given('I have a calculator')
def step_impl(context):
context.calculator = Calculator()
@when('I add "{x:Number}" and "{y:Number}"')
def step_impl(context, x, y):
assert isinstance(x, int)
assert isinstance(y, int)
context.calculator.add2(x, y)
@then('the calculator returns "{expected:Number}"')
def step_impl(context, expected):
assert isinstance(expected, int)
assert_that(context.calculator.result, equal_to(expected))
Note
Predefined Types
behave uses the parse module (inverse of Python string.format) under the hoods to parse parameters in step definitions. This leads to rather simple and readable parse expressions for step parameters.
See also Predefined Data Types in parse for more information. In addition, see also Data Types and User-defined Types for more information on defining and using user-defined data types.