Merge branch 'ISSUE-139-gherkin-terminal-reporter' of git://github.com/spinus/pytest-bdd into ISSUE-139-gherkin-terminal-reporter

This commit is contained in:
Omer Katz 2016-09-18 11:19:46 +03:00
commit 2e2b7a5c1b
7 changed files with 434 additions and 1 deletions

View File

@ -1,6 +1,11 @@
Changelog
=========
2.18.0
------
- Add gherkin terminal reporter (spinus + thedrow)
2.17.1
------

View File

@ -1126,6 +1126,13 @@ To have an output in json format:
py.test --cucumberjson=<path to json report>
To enable gherkin-formatted output on terminal, use
::
py.test --gherkin-terminal-reporter
Test code generation helpers
----------------------------

View File

@ -3,6 +3,6 @@
from pytest_bdd.steps import given, when, then
from pytest_bdd.scenario import scenario, scenarios
__version__ = '2.17.1'
__version__ = '2.18.0'
__all__ = [given.__name__, when.__name__, then.__name__, scenario.__name__, scenarios.__name__]

View File

@ -0,0 +1,98 @@
# -*- encoding: utf-8 -*-
from _pytest.terminal import TerminalReporter
def add_options(parser):
group = parser.getgroup("terminal reporting", "reporting", after="general")
group._addoption(
'--gherkin-terminal-reporter',
action="store_true",
dest="gherkin_terminal_reporter",
default=False,
help=(
"enable gherkin output"
)
)
def configure(config):
if config.option.gherkin_terminal_reporter:
# Get the standard terminal reporter plugin and replace it with our
current_reporter = config.pluginmanager.getplugin('terminalreporter')
if current_reporter.__class__ != TerminalReporter:
raise Exception("gherkin-terminal-reporter is not compatibile with any other terminal reporter."
"You can use only one terminal reporter."
"Currently '{0}' is used."
"Please decide to use one by deactivating {0} or gherkin-terminal-reporter."
.format(current_reporter.__class__))
gherkin_reporter = GherkinTerminalReporter(config)
config.pluginmanager.unregister(current_reporter)
config.pluginmanager.register(gherkin_reporter, 'terminalreporter')
if config.pluginmanager.getplugin("dsession"):
raise Exception("gherkin-terminal-reporter is not compatible with 'xdist' plugin.")
class GherkinTerminalReporter(TerminalReporter):
def __init__(self, config):
TerminalReporter.__init__(self, config)
def pytest_runtest_logstart(self, nodeid, location):
# Prevent locationline from being printed since we already
# show the module_name & in verbose mode the test name.
pass
def pytest_runtest_logreport(self, report):
rep = report
res = self.config.hook.pytest_report_teststatus(report=rep)
cat, letter, word = res
if not letter and not word:
# probably passed setup/teardown
return
if isinstance(word, tuple):
word, word_markup = word
else:
if rep.passed:
word_markup = {'green': True}
elif rep.failed:
word_markup = {'red': True}
elif rep.skipped:
word_markup = {'yellow': True}
feature_markup = {'blue': True}
scenario_markup = word_markup
if self.verbosity <= 0:
return TerminalReporter.pytest_runtest_logreport(self, rep)
elif self.verbosity == 1:
if hasattr(report, 'scenario'):
self.ensure_newline()
self._tw.write('Feature: ', **feature_markup)
self._tw.write(report.scenario['feature']['name'], **feature_markup)
self._tw.write('\n')
self._tw.write(' Scenario: ', **scenario_markup)
self._tw.write(report.scenario['name'], **scenario_markup)
self._tw.write(' ')
self._tw.write(word, **word_markup)
self._tw.write('\n')
else:
return TerminalReporter.pytest_runtest_logreport(self, rep)
elif self.verbosity > 1:
if hasattr(report, 'scenario'):
self.ensure_newline()
self._tw.write('Feature: ', **feature_markup)
self._tw.write(report.scenario['feature']['name'], **feature_markup)
self._tw.write('\n')
self._tw.write(' Scenario: ', **scenario_markup)
self._tw.write(report.scenario['name'], **scenario_markup)
self._tw.write('\n')
for step in report.scenario['steps']:
self._tw.write(' {} {}\n'.format(step['keyword'],
step['name']), **scenario_markup)
self._tw.write(' ' + word, **word_markup)
self._tw.write('\n\n')
else:
return TerminalReporter.pytest_runtest_logreport(self, rep)
self.stats.setdefault(cat, []).append(rep)

View File

@ -6,6 +6,7 @@ from . import given, when, then
from . import cucumber_json
from . import generation
from . import reporting
from . import gherkin_terminal_reporter
from .fixtures import *
@ -33,12 +34,14 @@ def pytest_addoption(parser):
"""Add pytest-bdd options."""
cucumber_json.add_options(parser)
generation.add_options(parser)
gherkin_terminal_reporter.add_options(parser)
@pytest.mark.trylast
def pytest_configure(config):
"""Configure all subplugins."""
cucumber_json.configure(config)
gherkin_terminal_reporter.configure(config)
def pytest_unconfigure(config):

View File

@ -0,0 +1,42 @@
Feature: Gherkin terminal reporter
Scenario: Should default output be the same as regular terminal reporter
Given there is gherkin scenario implemented
When tests are run
Then output must be formatted the same way as regular one
Scenario: Should verbose mode enable displaying feature and scenario names rather than test names in a single line
Given there is gherkin scenario implemented
When tests are run with verbose mode
Then output should contain single line feature description
And output should contain single line scenario description
Scenario: Should verbose mode preserve displaying of regular tests as usual
Given there is non-gherkin scenario implemented
When tests are run with verbose mode
Then output must be formatted the same way as regular one
Scenario: Should double verbose mode enable displaying of full gherkin scenario description
Given there is gherkin scenario implemented
When tests are run with very verbose mode
Then output must contain full gherkin scenario description
Scenario: Should error message be displayed when no scenario is found
Given there is gherkin scenario without implementation
When tests are run with any verbosity mode
Then output contains error about missing scenario implementation
Scenario: Should error message be displayed when no step is found
Given there is gherkin scenario partially implemented
When tests are run with any verbosity mode
Then output contains error about missing step implementation
Scenario: Should error message be displayed when error occurs during test execution
Given there is gherkin scenario with broken implementation
When tests are run with any verbosity mode
Then output contains error about missing scenario implementation
Scenario: Should local variables be displayed when --showlocals option is used
Given there is gherkin scenario with broken implementation
When tests are run with --showlocals
Then error traceback contains local variable descriptions

View File

@ -0,0 +1,278 @@
import re
import pytest
from pytest_bdd import scenario, given, when, then
@scenario('gherkin_terminal_reporter.feature',
'Should default output be the same as regular terminal reporter')
def test_Should_default_output_be_the_same_as_regular_terminal_reporter():
pass
@scenario('gherkin_terminal_reporter.feature',
'Should verbose mode enable displaying feature and scenario names rather than test names in a single line')
def test_Should_verbose_mode_enable_displaying_feature_and_scenario_names_rather_than_test_names_in_a_single_line():
pass
@scenario('gherkin_terminal_reporter.feature',
'Should verbose mode preserve displaying of regular tests as usual')
def test_Should_verbose_mode_preserve_displaying_of_regular_tests_as_usual():
pass
@scenario('gherkin_terminal_reporter.feature',
'Should double verbose mode enable displaying of full gherkin scenario description')
def test_Should_double_verbose_mode_enable_displaying_of_full_gherkin_scenario_description():
pass
@scenario('gherkin_terminal_reporter.feature',
'Should error message be displayed when no scenario is found')
def test_Should_error_message_be_displayed_when_no_scenario_is_found(verbosity_mode):
pass
@scenario('gherkin_terminal_reporter.feature',
'Should error message be displayed when no step is found')
def test_Should_error_message_be_displayed_when_no_step_is_found(verbosity_mode):
pass
@scenario('gherkin_terminal_reporter.feature',
'Should error message be displayed when error occurs during test execution')
def test_Should_error_message_be_displayed_when_error_occurs_during_test_execution(verbosity_mode):
pass
@scenario('gherkin_terminal_reporter.feature',
'Should local variables be displayed when --showlocals option is used')
def test_Should_local_variables_be_displayed_when___showlocals_option_is_used():
pass
@pytest.fixture(params=[0, 1, 2],
ids=['compact mode', 'line per test', 'verbose'])
def verbosity_mode(request):
return request.param, '-' + 'v' * request.param if request.param else ''
@pytest.fixture
def test_execution():
return {}
@given("there is non-gherkin scenario implemented")
def non_gherkin_test(testdir):
testdir.makepyfile(test_regular="""
def test_1():
pass
""")
@given("there is gherkin scenario implemented")
def gherkin_scenario(testdir):
testdir.makefile('.feature', test="""
Feature: Gherkin terminal output feature
Scenario: Scenario example 1
Given there is a bar
When the bar is accessed
Then world explodes
""")
testdir.makepyfile(test_gherkin="""
import pytest
from pytest_bdd import given, when, scenario, then
@given('there is a bar')
def a_bar():
return 'bar'
@when('the bar is accessed')
def the_bar_is_accessed():
pass
@then('world explodes')
def world_explodes():
pass
@scenario('test.feature', 'Scenario example 1')
def test_scenario_1():
pass
""")
@when("tests are run")
def tests_are_run(testdir, test_execution):
test_execution['regular'] = testdir.runpytest()
test_execution['gherkin'] = testdir.runpytest('--gherkin-terminal-reporter')
@then("output must be formatted the same way as regular one")
def output_must_be_the_same_as_regular_reporter(test_execution):
reg = test_execution['regular']
ghe = test_execution['gherkin']
assert reg.ret == 0
assert ghe.ret == 0
# last line can be different because of test execution time is printed
reg_lines = reg.stdout.lines if reg.stdout.lines[-1] else reg.stdout.lines[:-2]
reg_lines[-1] = re.sub(r' \d+\.\d+ ', ' X ', reg_lines[-1])
ghe_lines = ghe.stdout.lines if ghe.stdout.lines[-1] else ghe.stdout.lines[:-2]
ghe_lines[-1] = re.sub(r' \d+\.\d+ ', ' X ', ghe_lines[-1])
for l1, l2 in zip(reg_lines, ghe_lines):
assert l1 == l2
@when("tests are run with verbose mode")
def tests_are_run_with_verbose_mode(testdir, test_execution):
test_execution['regular'] = testdir.runpytest('-v')
test_execution['gherkin'] = testdir.runpytest('--gherkin-terminal-reporter', '-v')
@when("tests are run with very verbose mode")
def tests_are_run_with_very_verbose_mode(testdir, test_execution):
test_execution['regular'] = testdir.runpytest('-vv')
test_execution['gherkin'] = testdir.runpytest('--gherkin-terminal-reporter', '-vv')
@then("output should contain single line feature description")
def output_should_contain_single_line_feature_description(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret == 0
ghe.stdout.fnmatch_lines('Feature: Gherkin terminal output feature')
@then("output should contain single line scenario description")
def output_should_contain_single_line_scenario_description(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret == 0
ghe.stdout.fnmatch_lines('*Scenario: Scenario example 1 PASSED')
@then("output must contain full gherkin scenario description")
def output_should_contain_full_gherkin_scenario_description(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret == 0
ghe.stdout.fnmatch_lines('*Scenario: Scenario example 1')
ghe.stdout.fnmatch_lines('*Given there is a bar')
ghe.stdout.fnmatch_lines('*When the bar is accessed')
ghe.stdout.fnmatch_lines('*Then world explodes')
ghe.stdout.fnmatch_lines('*PASSED')
@given('there is gherkin scenario without implementation')
def gherkin_scenario_without_implementation(testdir):
testdir.makefile('.feature', test="""
Feature: Gherkin terminal output feature
Scenario: Scenario example 1
Given there is a bar
When the bar is accessed
Then world explodes
""")
testdir.makepyfile(test_gherkin="""
import pytest
from pytest_bdd import scenarios
scenarios('.')
""")
@when('tests are run with any verbosity mode')
def tests_are_run_with_any_verbosity_mode(
test_execution, verbosity_mode, testdir,
gherkin_scenario_without_implementation):
# test_execution['gherkin'] = testdir.runpytest(
# '--gherkin-terminal-reporter', '-vv')
if verbosity_mode[1]:
test_execution['gherkin'] = testdir.runpytest(
'--gherkin-terminal-reporter', verbosity_mode[1])
else:
test_execution['gherkin'] = testdir.runpytest(
'--gherkin-terminal-reporter')
@then('output contains error about missing scenario implementation')
def output_contains_error_about_missing_scenario_implementation(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret
ghe.stdout.fnmatch_lines('''*StepDefinitionNotFoundError: Step definition is not found: Given "there is a bar". '''
'''Line 3 in scenario "Scenario example 1"*''')
@given('there is gherkin scenario partially implemented')
def partially_implemented_gherkin_scenario(testdir):
testdir.makefile('.feature', test="""
Feature: Gherkin terminal output feature
Scenario: Scenario example 1
Given there is a bar
When the bar is accessed
Then world explodes
""")
testdir.makepyfile(test_gherkin="""
import pytest
from pytest_bdd import given, when, scenario, then
@given('there is a bar')
def a_bar():
return 'bar'
@when('the bar is accessed')
def the_bar_is_accessed():
pass
@scenario('test.feature', 'Scenario example 1')
def test_scenario_1():
pass
""")
@then('output contains error about missing step implementation')
def output_contains_error_about_missing_step_implementation(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret
ghe.stdout.fnmatch_lines('''*StepDefinitionNotFoundError: Step definition is not found: Given "there is a bar". '''
'''Line 3 in scenario "Scenario example 1"*''')
@given('there is gherkin scenario with broken implementation')
def there_is_gherkin_scenario_with_broken_implementation(testdir):
testdir.makefile('.feature', test="""
Feature: Gherkin terminal output feature
Scenario: Scenario example 1
Given there is a bar
When the bar is accessed
Then world explodes
""")
testdir.makepyfile(test_gherkin="""
import pytest
from pytest_bdd import given, when, scenario, then
@given('there is a bar')
def a_bar(request):
return 'bar'
@when('the bar is accessed')
def the_bar_is_accessed(request):
local_var = 'value2'
raise Exception("ERROR")
@scenario('test.feature', 'Scenario example 1')
def test_scenario_1():
pass
""")
@when('tests are run with --showlocals')
def tests_are_run_with___showlocals(test_execution, testdir):
test_execution['gherkin'] = testdir.runpytest('--gherkin-terminal-reporter', '--showlocals')
@then('error traceback contains local variable descriptions')
def error_traceback_contains_local_variable_descriptions(test_execution):
ghe = test_execution['gherkin']
assert ghe.ret
ghe.stdout.fnmatch_lines('''request*=*<FixtureRequest for *''')
ghe.stdout.fnmatch_lines('''local_var*=*''')