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))