Tutorial 2: Natural Language¶
- Goal
Use natural language when writing tests.
Hint
This example is based on the Ninja Survival Rate examples from [SecretNinja10].
Write the Feature Test¶
# file:features/tutorial02_natural_language.feature
Feature: Fight or Flight (Natural Language, tutorial02)
In order to increase the ninja survival rate,
As a ninja commander
I want my ninjas to decide whether to take on an opponent
based on their skill levels.
Scenario: Weaker opponent
Given the ninja has a third level black-belt
When attacked by a samurai
Then the ninja should engage the opponent
Scenario: Stronger opponent
Given the ninja has a third level black-belt
When attacked by Chuck Norris
Then the ninja should run for his life
Provide the Test Automation¶
# file:features/steps/step_tutorial02.py
# ----------------------------------------------------------------------------
# STEPS:
# ----------------------------------------------------------------------------
from behave import given, when, then
from hamcrest import assert_that, equal_to, is_not
@given('the ninja has a {achievement_level}')
def step_the_ninja_has_a(context, achievement_level):
context.ninja_fight = NinjaFight(achievement_level)
@when('attacked by a {opponent_role}')
def step_attacked_by_a(context, opponent_role):
context.ninja_fight.opponent = opponent_role
@when('attacked by {opponent}')
def step_attacked_by(context, opponent):
context.ninja_fight.opponent = opponent
@then('the ninja should {reaction}')
def step_the_ninja_should(context, reaction):
assert_that(reaction, equal_to(context.ninja_fight.decision()))
Provide the Domain Model¶
Normally, the domain model is the
class-under-test (CUT)
subsystem-under-test
system-under-test (SUT)
It contains the business logic that describes the behaviour of the system. The thin test automation layer from above (step definitions) just interacts with it. The domain model normally preexists (in another Python module/package) and you do not have to write it.
# file:features/steps/step_tutorial02.py
# ----------------------------------------------------------------------------
# PROBLEM DOMAIN:
# ----------------------------------------------------------------------------
class NinjaFight(object):
"""
Domain model for ninja fights.
"""
# pylint: disable=R0903
def __init__(self, with_ninja_level=None):
self.with_ninja_level = with_ninja_level
self.opponent = None
def decision(self):
"""
Business logic how a Ninja should react to increase his survival rate.
"""
assert self.with_ninja_level is not None
assert self.opponent is not None
if self.opponent == "Chuck Norris":
return "run for his life"
if "black-belt" in self.with_ninja_level:
return "engage the opponent"
else:
return "run for his life"
Run the Feature Test¶
When you run the feature file from above:
$ behave ../features/tutorial02_natural_language.feature Feature: Fight or Flight (Natural Language, tutorial02) # ../features/tutorial02_natural_language.feature:1 In order to increase the ninja survival rate, As a ninja commander I want my ninjas to decide whether to take on an opponent based on their skill levels. Scenario: Weaker opponent # ../features/tutorial02_natural_language.feature:8 Given the ninja has a third level black-belt # ../features/steps/step_tutorial02.py:57 When attacked by a samurai # ../features/steps/step_tutorial02.py:61 Then the ninja should engage the opponent # ../features/steps/step_tutorial02.py:69 Scenario: Stronger opponent # ../features/tutorial02_natural_language.feature:13 Given the ninja has a third level black-belt # ../features/steps/step_tutorial02.py:57 When attacked by Chuck Norris # ../features/steps/step_tutorial02.py:65 Then the ninja should run for his life # ../features/steps/step_tutorial02.py:69 1 feature passed, 0 failed, 0 skipped 2 scenarios passed, 0 failed, 0 skipped 6 steps passed, 0 failed, 0 skipped, 0 undefined Took 0m0.001s
The Complete Picture¶
# file:features/steps/step_tutorial02.py
# -*- coding: UTF-8 -*-
# MISSING-DOCSTRING: pylint: disable=C0111
"""
Based on ``behave tutorial``
Feature: Fight or Flight (Natural Language)
In order to increase the ninja survival rate, #< Business goal
As a ninja commander #< Role
I want my ninjas to decide whether to take on an opponent #< Benefit
based on their skill levels
Scenario: Weaker opponent
Given the ninja has a third level black-belt
When attacked by a samurai
Then the ninja should engage the opponent
Scenario: Stronger opponent
Given the ninja has a third level black-belt
When attacked by Chuck Norris
Then the ninja should run for his life
"""
# @mark.domain_model
# ----------------------------------------------------------------------------
# PROBLEM DOMAIN:
# ----------------------------------------------------------------------------
class NinjaFight(object):
"""
Domain model for ninja fights.
"""
# pylint: disable=R0903
def __init__(self, with_ninja_level=None):
self.with_ninja_level = with_ninja_level
self.opponent = None
def decision(self):
"""
Business logic how a Ninja should react to increase his survival rate.
"""
assert self.with_ninja_level is not None
assert self.opponent is not None
if self.opponent == "Chuck Norris":
return "run for his life"
if "black-belt" in self.with_ninja_level:
return "engage the opponent"
else:
return "run for his life"
# @mark.steps
# ----------------------------------------------------------------------------
# STEPS:
# ----------------------------------------------------------------------------
from behave import given, when, then
from hamcrest import assert_that, equal_to, is_not
@given('the ninja has a {achievement_level}')
def step_the_ninja_has_a(context, achievement_level):
context.ninja_fight = NinjaFight(achievement_level)
@when('attacked by a {opponent_role}')
def step_attacked_by_a(context, opponent_role):
context.ninja_fight.opponent = opponent_role
@when('attacked by {opponent}')
def step_attacked_by(context, opponent):
context.ninja_fight.opponent = opponent
@then('the ninja should {reaction}')
def step_the_ninja_should(context, reaction):
assert_that(reaction, equal_to(context.ninja_fight.decision()))