BDD¶
Quick Start¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
@test_plan(name="Example Gherkin Testplan")
def main(plan):
factory = BDDTestSuiteFactory("features", default_parser=SimpleParser)
plan.add(
MultiTest(
name="Example Gherkin Test",
description="Example Gherkin Suite",
suites=factory.create_suites(),
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
features/first.feature¶
Feature: Example Gherkin Testsuite
Just a simple sum feature with some testcase
Scenario: 1 + 1
Given we have two number: 1 and 1
When we sum the numbers
Then the result is: 2
Scenario: 2 + 2
Same as above just now with 2+2
Given we have two number: 2 and 2
When we sum the numbers
Then the result is: 4
features/steps/steps.py¶
from testplan.testing.bdd.step_registry import Given, When, Then
@Given("we have two number: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@When("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
@Then("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))
Arguments¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Arguments"
DESCRIPTION = (
"Example to show how complex arguments can be passed to step definitions"
)
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
arguments.feature¶
Feature: Arguments Examples
Scenario: Docstring
Given we log a complex formatted string:
"""
This is a multi line formatted string
with leading spaces
expected to be logged as is.
"""
Scenario: Table Data
Given we have a table and we nicely log it:
| name | phone number |
| John | +361234564 |
| Jane | +361234561 |
| Max | unknown |
Scenario: Argument mixed with captured parameters
Given we fill the format with name: John
"""
Hello %s
"""
Then the result is "Hello John"
arguments.steps.py¶
from testplan.testing.bdd.step_registry import step
@step("we log a complex formatted string:")
def step_definition(env, result, context, argument):
# argument here is an str as it is passed as a python stile docstring
result.log(argument)
@step("we have a table and we nicely log it:")
def step_definition(env, result, context, argument):
# argument here is a DataTable
result.table.log(list(argument.rows()))
@step("we fill the format with name: {name}")
def step_definition(env, result, context, argument, name):
context.result = argument % name
@step('the result is "{expected}"')
def step_definition(env, result, context, expected):
result.equal(context.result, expected)
Background¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Background"
DESCRIPTION = "Example to show how background can be used to perform the same steps before scenarios"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
background.feature¶
Feature: Background Example
This example show how background is executed before each scenario
This way it is similar to pre_testcase
Background:
Given we have first_name as John
Given we have last_name as Doe
Scenario: Salute in English
When we say hi
Then it sounds: "Hi John Doe"
Scenario: Salute in Hungarian
When we say hello
Then it sounds: "Hello Doe John"
background.steps.py¶
from testplan.testing.bdd.step_registry import step
# This is the function to test
def salute(format_str, first_name, last_name):
return format_str.format(first_name=first_name, last_name=last_name)
# With these formats of salute
FORMATS = {
"hi": "Hi {first_name} {last_name}",
"hello": "Hello {last_name} {first_name}",
}
@step("we have {key} as {value}")
def step_definition(env, result, context, key, value):
context[key] = value
@step("we say {salute_format}")
def step_definition(env, result, context, salute_format):
context.result = salute(
FORMATS[salute_format], context.first_name, context.last_name
)
@step('it sounds: "{expected}"')
def step_definition(env, result, context, expected):
result.equal(context.result, expected)
Context Resolution¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
from testplan.testing.bdd.bdd_tools import ContextResolver
NAME = "Context Resolution"
DESCRIPTION = "Example to show how to refer context values from gherkin "
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".",
default_parser=SimpleParser,
feature_linked_steps=True,
resolver=ContextResolver(),
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
context_resolution.feature¶
Feature: Hello world with random names
Simple hello world example where the name is resolved from context
Scenario: single name
Use a random name in steps through contex
# this generate a random name and store in context.name
Given a random name as name
# {{name}} will be replaced from context before matching the step definition
When salute is called with "{{name}}"
Then the result is "Hello {{name}}"
Scenario: double name
Use two random names with the the same steps as before
Given a random name as firstName
Given a random name as middleName
When salute is called with "{{firstName}} {{middleName}}"
Then the result is "Hello {{firstName}} {{middleName}}"
Scenario: with argument
Resolution works in Arguments as well
Given a random name as name
When salute is called with:
"""
Mr/Ms {{name}}
"""
Then the result is "Hello Mr/Ms {{name}}"
Scenario: with table
Works even if the argument is a table
Given a random name as firstName
Given a random name as middleName
When salute is called with names:
| [firstname] | [middlename] |
| {{firstName}} | {{middleName}} |
Then the result is "Hello {{firstName}} {{middleName}}"
Scenario: with dict table
Given a random name as firstName
Given a random name as middleName
When salute is called with name parts:
| [key] | [value] |
| firstname | {{firstName}} |
| midname | {{middleName}} |
| lastname | Smith |
Then the result is "Hello {{firstName}} {{middleName}} Smith"
Scenario: With nested structure
Given a json document as person:
"""
{
"name": {
"firstName": "John",
"lastName": "Doe"
},
"dateOfBirth": "1911 November"
}
"""
When salute is called with "{{person.name.firstName}} {{person.name.lastName}} from {{person.dateOfBirth}}"
Then the result is "Hello {{person.name.firstName}} {{person.name.lastName}} from {{person.dateOfBirth}}"
context_resolution.steps.py¶
import json
import random
from testplan.testing.bdd.step_registry import step
NAMES = ["Jane", "John", "Marvin", "Grace"]
def salute(name):
return "Hello {}".format(name)
@step('"{value}" is stored in the context as {name}')
def step_definition(env, result, context, value, name):
context[name] = value
@step("a random name as {name}")
def step_definition(env, result, context, name):
context[name] = random.choice(NAMES)
result.log("Random {}: {}".format(name, context[name]))
@step('salute is called with "{name}"')
def step_definition(env, result, context, name):
context.result = salute(name)
@step("salute is called with:")
def step_definition(env, result, context, arg):
context.result = salute(arg)
@step("salute is called with names:")
def step_definition(env, result, context, args):
row = next(args.rows())
context.result = salute("{} {}".format(row.firstname, row.middlename))
@step("salute is called with name parts:")
def step_definition(env, result, context, args):
parts = args.dict()
context.result = salute(
"{} {} {}".format(
parts["firstname"], parts["midname"], parts["lastname"]
)
)
@step('the result is "{expected}"')
def step_definition(env, result, context, expected):
result.equal(context.result, expected)
@step("a json document as {doc_name}:")
def step_definition(env, result, context, json_input, doc_name):
context[doc_name] = json.loads(json_input)
context_resolution_indexable.feature¶
Feature: Indexable mixin example
Scenario: index into a class which is Indexable
Given the example indexable is in the context as my_example_indexable
#simple members
Then {{my_example_indexable.a}} == 12
Then {{my_example_indexable.b}} == 13
# go deeper in list
Then {{my_example_indexable.l.0}} == 1
Then {{my_example_indexable.l.1}} == 2
Then {{my_example_indexable.l.2}} == 5
# check a dictionary
Then {{my_example_indexable.d.a}} == 12
Then {{my_example_indexable.d.b}} == 13
Then {{my_example_indexable.d.l}} == {{my_example_indexable.l}}
context_resolution_indexable.steps.py¶
import json
import random
from testplan.testing.bdd.step_registry import step
class Indexable:
def __getitem__(self, item):
return self.__dict__[item]
class indexexample(Indexable):
def __init__(self):
self.a = 12
self.b = 13
self.l = [1, 2, 5]
self.d = {"a": 12, "b": 13, "l": [1, 2, 5]}
@step("the example indexable is in the context as {name}")
def step_definition(env, result, context, name):
context[name] = indexexample()
@step("{actual} == {expected}")
def step_definition(env, result, context, actual, expected):
# note that this is string comparision as coming from the feature file
result.equal(actual, expected)
Import Steps¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
from testplan.testing.bdd.bdd_tools import ContextResolver
NAME = "Import Step Definitions"
DESCRIPTION = "Example to show how to import step definitions, best shown with feature linked"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".",
default_parser=SimpleParser,
feature_linked_steps=True,
resolver=ContextResolver(),
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
common.py¶
from testplan.testing.bdd.step_registry import Given, Then
@Given("my name is {name}")
def step_definition(env, result, context, name):
context.name = name
@Then("I hear: {salute}")
def step_definition(env, result, context, salute):
result.equal(context.salute, salute)
one.feature¶
@feature_linked @smoke
Feature: Salute in English
Scenario: Salute
Given my name is John
When one salute
Then I hear: Hello John
one.steps.py¶
from testplan.testing.bdd.step_registry import When, import_steps
import_steps("common.py")
@When("one salute")
def step_definition(env, result, context):
context.salute = "Hello {}".format(context.name)
two.feature¶
@feature_linked @smoke
Feature: Salute in Hungarian
Scenario: Salute
Given my name is Otto
When one salute
Then I hear: Szia Otto
two.steps.py¶
from testplan.testing.bdd.step_registry import When, import_steps
import_steps("common.py")
@When("one salute")
def step_definition(env, result, context):
context.salute = "Szia {}".format(context.name)
Common Steps¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
@test_plan(name="Example Gherkin Testplan")
def main(plan):
factory1 = BDDTestSuiteFactory(
"features/feature1",
default_parser=SimpleParser,
common_step_dirs=["features/steps"],
)
factory2 = BDDTestSuiteFactory(
"features/feature2",
default_parser=SimpleParser,
common_step_dirs=["features/steps"],
)
plan.add(
MultiTest(
name="Example Gherkin Test",
description="Example Gherkin Suites",
suites=[*factory1.create_suites(), *factory2.create_suites()],
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
features/steps/features.py¶
from testplan.testing.bdd.step_registry import Given, Then
@Given("we have two numbers: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@Then("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))
features/feature1/feature11.feature¶
Feature: addition_1
Just a simple sum feature with some testcase
Scenario: 1 + 1
Given we have two numbers: 1 and 1
When we sum the numbers
Then the result is: 2
Scenario: 2 + 2
Same as above just now with 2+2
Given we have two numbers: 2 and 2
When we sum the numbers
Then the result is: 4
features/feature1/feature12.feature¶
Feature: addition_2
A complex sum feature with some testcase
Scenario: 1 + 2
Given we have two numbers: 1 and 2
When we sum the numbers
Then the result is: 3
Scenario: 2 + 3
Same as above just now with 2+3
Given we have two numbers: 2 and 3
When we sum the numbers
Then the result is: 5
features/feature1/steps/feature1.py¶
from testplan.testing.bdd.step_registry import When
@When("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
features/feature2/feature2.feature¶
Feature: subtraction
Just a simple subtraction feature with some testcase
Scenario: 2 - 1
Given we have two numbers: 2 and 1
When we subtract the numbers
Then the result is: 1
Scenario: 5 - 3
Same as above just now with 5-3
Given we have two numbers: 5 and 3
When we subtract the numbers
Then the result is: 2
features/feature2/steps/feature2.py¶
from testplan.testing.bdd.step_registry import When
@When("we subtract the numbers")
def step_definition(env, result, context):
context.result = context.numbers[0] - context.numbers[1]
Known To Fail¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Known To Fail"
DESCRIPTION = "Example to show how to deal with scnearios known to fail"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
common.py¶
from testplan.testing.bdd.step_registry import step
@step("we have two number: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@step("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
@step("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))
known_failure_feature.feature¶
@KNOWN_TO_FAIL: All testcase of this feature is known to fail
@unit
Feature: sum adding two number The wrong way expected to fail
Scenario: add 1 and -1
Check if 1-1 == 1
Given we have two number: 1 and -1
When we sum the numbers
Then the result is: 1
Scenario: add 1 and -2
Check if 1-2 == 1
Given we have two number: 1 and -2
When we sum the numbers
Then the result is: 1
known_failure_feature.steps.py¶
from testplan.testing.bdd.step_registry import import_steps
import_steps("common.py")
known_failure_scenarios.feature¶
@unit
Feature: sum adding two number The wrong way with xfail scenarios
@KNOWN_TO_FAIL:
Scenario: add 1 and -3
Check if 1-3 == 1
Given we have two number: 1 and -3
When we sum the numbers
Then the result is: 1
@KNOWN_TO_FAIL: A simple Fail with comment
Scenario: add 1 and -1
Check if 1-1 == 1
Given we have two number: 1 and -1
When we sum the numbers
Then the result is: 1
Scenario Outline: sums
Check if <num1> + <num2> == <expected>
Given we have two number: <num1> and <num2>
When we sum the numbers
Then the result is: <expected>
@KNOWN_TO_FAIL: Failing scenario outline examples
Examples: Failing examples
| num1 | num2 | expected |
| 1 | -1 | 1 |
| 1 | -2 | 1 |
| 1 | -3 | 1 |
Examples: Non failing examples
| num1 | num2 | expected |
| 1 | -1 | 0 |
| 1 | -2 | -1 |
| 1 | -3 | -2 |
known_failure_scenarios.steps.py¶
from testplan.testing.bdd.step_registry import import_steps
import_steps("common.py")
known_failure_fixed.feature¶
Feature: Expected to fail but pass
This example demonstrate a known to fail scenario which do not fail animore
so testplan will just fail the test to show that there is something to check
@KNOWN_TO_FAIL: That actually do not fail
Scenario: expected to fail but not failing so failing
Check if 1-2 == 1
Given we have two number: 1 and -2
When we sum the numbers
Then the result is: -1
known_failure_fixed.steps.py¶
from testplan.testing.bdd.step_registry import import_steps
import_steps("common.py")
Labels¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Labels"
DESCRIPTION = "Examples for labels of all kind"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
labels.feature¶
@has_outline @fast
Feature: Labels Example
@single
Scenario: add 1 and 2
Given we have two number: 1 and 2
When we sum the numbers
Then the result is: 3
@parametrized
Scenario Outline: add two number
Check if sum can add two number
Given we have two number: <a> and <b>
When we sum the numbers
Then the result is: <expected>
@positive
Examples: when both positive
| a | b | expected |
| 1 | 1 | 2 |
| 2 | 2 | 4 |
| 123 | 321 | 444 |
@negative
Examples: when one negative
| a | b | expected |
| 1 | -1 | 0 |
| 2 | -2 | 0 |
| -123 | 321 | 198 |
labels.steps.py¶
from testplan.testing.bdd.step_registry import step
@step("we have two number: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@step("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
@step("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))
Parsers¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
NAME = "Special Scenarios"
DESCRIPTION = "Example to show how to change parsers in step definitions"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(".", feature_linked_steps=True)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
parsers.feature¶
Feature: Parsers example
Show how parsers can be used to match sentences and capture parameters
Scenario: Simple parser
Simple parser can parse with the "parse" python library,
- all sentence with _SP will be matched by SimpleParser
- all sentence with _RP will be matched by RegExParser
Given _SP explicit match
Given _RP explicit match
# with capture
Given _SP explicit match and log name: Simple John
Given _RP explicit match and log name: Regular Gil
Given _RP as default match
Given _RP as default match and log name: Regular Gil
Given _SP as override match
Given _SP as override match and log name: Simple John
# RP can do regexp tricks easily to match different sentence to the same
Given _RP to match this
And _RP and this too
parsers.steps.py¶
from testplan.testing.bdd.parsers import SimpleParser, RegExParser
from testplan.testing.bdd.step_registry import step, set_actual_parser
# Using a parser can be explicit
@step(SimpleParser("_SP explicit match"))
def step_definition(env, result, context):
pass
@step(RegExParser("^_RP explicit match$"))
def step_definition(env, result, context):
pass
# it can capture things from the sentence
@step(SimpleParser("_SP explicit match and log name: {name}"))
def step_definition(env, result, context, name):
result.log(name)
@step(RegExParser("_RP explicit match and log name: (?P<name>.*)"))
def step_definition(env, result, context, name):
result.log(name)
# The default is RegExParser
@step("^_RP as default match$")
def step_definition(env, result, context):
pass
@step("_RP as default match and log name: (?P<name>.*)")
def step_definition(env, result, context, name):
result.log(name)
# one can override the parser for a portion of the step definition file
set_actual_parser(SimpleParser)
@step("_SP as override match")
def step_definition(env, result, context):
pass
@step("_SP as override match and log name: {name}")
def step_definition(env, result, context, name):
result.log(name)
# and change back to RegExParser
set_actual_parser(RegExParser)
@step("_RP (?:to match this|and this too)")
def step_definition(env, result, context):
result.log("multiple sentence matcher")
Scenario Outline¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Scenario Outline"
DESCRIPTION = "Example to show parametrized scenarios with Scenario Outline"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
scenario_outline.feature¶
Feature: Scenario Outline Example
Scenario Outline: add two number
Check if sum can add two number
Given we have two number: <a> and <b>
When we sum the numbers
Then the result is: <expected>
Examples: when both positive
| a | b | expected |
| 1 | 1 | 2 |
| 2 | 2 | 4 |
| 123 | 321 | 444 |
Examples: when one negative
| a | b | expected |
| 1 | -1 | 0 |
| 2 | -2 | 0 |
| -123 | 321 | 198 |
scenario_outline.steps.py¶
from testplan.testing.bdd.step_registry import step
@step("we have two number: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@step("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
@step("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))
Special Scenarios¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
from testplan.testing.bdd.bdd_tools import ContextResolver
NAME = "Special Scenarios"
DESCRIPTION = "Example to show how how to hook up setup/teardown pre_testcase/post_testcase"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".",
default_parser=SimpleParser,
feature_linked_steps=True,
resolver=ContextResolver(),
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
special_scenarios.feature¶
Feature: Special Scenarios example
setup/teardown: Running after before/after the normal scenarios in the Feature file
These functions has access to a context, which will be the base context
for every scenario runs.
pre_testcase/post_testcase: Run around any scenario, and has access to the same context
as the scenarios
Scenario Outline: Testcase <name>
Simple scenario to see that all 3 scenario from here is running
within the pre/post testcase wrappers
When we log Hello from Testcase <name>
Then we log Again Hello from Testcase <name>
Examples:
| name |
| 1 |
| 2 |
| three |
Scenario: Acces context
To demonstrate a scenario can access values from context put there by
setup/pre_testcase
# check value saved by pre_testcase
When salute is called with "{{name_from_pre}}"
Then the result is "Hello Rozi Kis"
# check value saved by setup
When salute is called with "{{firstName_from_setup}}"
Then the result is "Hello Rozi"
Scenario Outline: Modify context <idx>
The context prepared in setup is always copied for scenario runs, so
cahnges to the context does not get persisted between scenarios, even
though the setup runs just once
# First check we have the expected values
When salute is called with "{{name_from_pre}}"
Then the result is "Hello Rozi Kis"
When salute is called with "{{firstName_from_setup}}"
Then the result is "Hello Rozi"
# Now modify them
Given "Mari" is stored in the context as firstName_from_setup
Given "{{firstName_from_setup}} Nagy" is stored in the context as name_from_pre
When salute is called with "{{name_from_pre}}"
Then the result is "Hello Mari Nagy"
When salute is called with "{{firstName_from_setup}}"
Then the result is "Hello Mari"
# we run multiple time to see that the modification does not persist
Examples:
| idx |
| 1 |
| 2 |
Scenario: setup
Given "Rozi" is stored in the context as firstName_from_setup
And we log In setup
And we log name: {{firstName_from_setup}}
Scenario: teardown
And we log In teardown
And we log name: {{firstName_from_setup}}
Scenario: pre_testcase
Given "{{firstName_from_setup}} Kis" is stored in the context as name_from_pre
And we log In pre_testcase
And we log name: {{name_from_pre}}
Scenario: post_testcase
And we log In post_testcase
And we log name: {{firstName_from_setup}}
And we log name: {{name_from_pre}}
special_scenarios.steps.py¶
from testplan.testing.bdd.step_registry import step
def salute(name):
return "Hello {}".format(name)
@step("we log {message}")
def step_definition(env, result, context, message):
result.log(message)
@step('salute is called with "{name}"')
def step_definition(env, result, context, name):
context.result = salute(name)
@step('"{value}" is stored in the context as {name}')
def step_definition(env, result, context, value, name):
context[name] = value
@step('the result is "{expected}"')
def step_definition(env, result, context, expected):
result.equal(context.result, expected)
Tcp¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan.common.utils.context import context
from testplan.testing.multitest import MultiTest
from testplan.testing.multitest.driver.tcp import TCPServer, TCPClient
from testplan import test_plan
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "BDD style TCP example"
DESCRIPTION = "Example to show driver usage in BDD style"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME,
description=DESCRIPTION,
suites=factory.create_suites(),
environment=[
TCPServer(name="server"),
TCPClient(
name="client",
host=context("server", "{{host}}"),
port=context("server", "{{port}}"),
),
],
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
tcp.feature¶
Feature: TCP example
Scenario: setup
Given the server is accepting connectons
Scenario Outline: Send/Receive <message> --- <response>
When client send: <message>
Then the server receive: <message>
When the server respond with: <response>
Then the client got: <response>
Examples:
| message | response |
| Hello | World |
| Test | Plan |
tcp.steps.py¶
from testplan.testing.bdd.step_registry import step
@step("the server is accepting connectons")
def step_definition(env, result, context):
# Server accepts client connection.
env.server.accept_connection()
@step("client send: {message}")
def step_definition(env, result, context, message):
context.bytes_sent = env.client.send_text(message)
@step("the server receive: {message}")
def step_definition(env, result, context, message):
received = env.server.receive_text(size=context.bytes_sent)
result.equal(received, message, "Server received")
@step("the server respond with: {response}")
def step_definition(env, result, context, response):
context.bytes_sent = env.server.send_text(response)
@step("the client got: {response}")
def step_definition(env, result, context, response):
received = env.client.receive_text(size=context.bytes_sent)
result.equal(received, response, "Client received")
Parallel Execution¶
- Required files:
test_plan.py¶
#!/usr/bin/env python
from testplan import test_plan
from testplan.testing.multitest import MultiTest
from testplan.testing.bdd import BDDTestSuiteFactory
from testplan.testing.bdd.parsers import SimpleParser
NAME = "Parallel execution"
DESCRIPTION = "Example to show how to execute scnearios parallel"
@test_plan(name="BDD Example")
def main(plan):
factory = BDDTestSuiteFactory(
".", default_parser=SimpleParser, feature_linked_steps=True
)
plan.add(
MultiTest(
name=NAME, description=DESCRIPTION, suites=factory.create_suites()
)
)
if __name__ == "__main__":
import sys
sys.exit(not main())
parallel.feature¶
Feature: Parallel execution example
Scenario: pre_testcase
Given thread id is logged
Scenario Outline: add two number
Check if sum can add two number
Given we have two number: <a> and <b>
When we sum the numbers
Then the result is: <expected>
@TP_EXECUTION_GROUP: group1
Examples: when both positive
| a | b | expected |
| 1 | 1 | 2 |
| 2 | 2 | 4 |
| 123 | 321 | 444 |
Examples: when one negative
| a | b | expected |
| 1 | -1 | 0 |
| 2 | -2 | 0 |
| -123 | 321 | 198 |
@TP_EXECUTION_GROUP: group3
Scenario: add two number
Check if sum can add two number
Given we have two number: 5 and 5
When we sum the numbers
Then the result is: 10
parallel.steps.py¶
import threading
from testplan.testing.bdd.step_registry import step
@step("thread id is logged")
def step_definition(env, result, context):
result.log(
f"{threading.current_thread().name}: {threading.current_thread().ident}"
)
@step("we have two number: {a} and {b}")
def step_definition(env, result, context, a, b):
context.numbers = (int(a), int(b))
@step("we sum the numbers")
def step_definition(env, result, context):
context.result = sum(context.numbers)
@step("the result is: {expected}")
def step_definition(env, result, context, expected):
result.equal(context.result, int(expected))