forked from test_framework/pytest-bdd
Merge pull request #227 from mjholtkamp/feature/json_cucumber_expand_option
cucumber.json expanded format
This commit is contained in:
commit
5a5165a7e0
|
@ -18,5 +18,6 @@ These people have contributed to `pytest-bdd`, in alphabetical order:
|
|||
* `Harro van der Klauw <hvdklauw@gmail.com>`_
|
||||
* `Laurence Rowe <l@lrowe.co.uk>`_
|
||||
* `Leonardo Santagada <santagada@github.com>`_
|
||||
* `Michiel Holtkamp <github@elfstone.nl>`_
|
||||
* `Robin Pedersen <ropez@github.com>`_
|
||||
* `Sergey Kraynev <sergejyit@gmail.com>`_
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
2.19.0
|
||||
------
|
||||
|
||||
- Added --cucumber-json-expanded option for explicit selection of expanded format (mjholtkamp)
|
||||
- Step names are filled in when --cucumber-json-expanded is used (mjholtkamp)
|
||||
|
||||
2.18.2
|
||||
------
|
||||
|
||||
|
|
|
@ -1133,6 +1133,12 @@ To have an output in json format:
|
|||
|
||||
py.test --cucumberjson=<path to json report>
|
||||
|
||||
This will output an expanded (meaning scenario outlines will be expanded to several scenarios) cucumber format.
|
||||
To also fill in parameters in the step name, you have to explicitly tell pytest-bdd to use the expanded format:
|
||||
|
||||
::
|
||||
|
||||
py.test --cucumberjson=<path to json report> --cucumberjson-expanded
|
||||
|
||||
To enable gherkin-formatted output on terminal, use
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import os
|
|||
import time
|
||||
|
||||
import py
|
||||
import re
|
||||
import six
|
||||
|
||||
from .feature import force_unicode
|
||||
|
@ -27,12 +28,21 @@ def add_options(parser):
|
|||
help="create cucumber json style report file at given path.",
|
||||
)
|
||||
|
||||
group._addoption(
|
||||
"--cucumberjson-expanded",
|
||||
"--cucumber-json-expanded",
|
||||
action="store_true",
|
||||
dest="expand",
|
||||
default=False,
|
||||
help="expand scenario outlines into scenarios and fill in the step names",
|
||||
)
|
||||
|
||||
|
||||
def configure(config):
|
||||
cucumber_json_path = config.option.cucumber_json_path
|
||||
# prevent opening json log on slave nodes (xdist)
|
||||
if cucumber_json_path and not hasattr(config, "slaveinput"):
|
||||
config._bddcucumberjson = LogBDDCucumberJSON(cucumber_json_path)
|
||||
config._bddcucumberjson = LogBDDCucumberJSON(cucumber_json_path, expand=config.option.expand)
|
||||
config.pluginmanager.register(config._bddcucumberjson)
|
||||
|
||||
|
||||
|
@ -47,10 +57,11 @@ class LogBDDCucumberJSON(object):
|
|||
|
||||
"""Logging plugin for cucumber like json output."""
|
||||
|
||||
def __init__(self, logfile):
|
||||
def __init__(self, logfile, expand=False):
|
||||
logfile = os.path.expanduser(os.path.expandvars(logfile))
|
||||
self.logfile = os.path.normpath(os.path.abspath(logfile))
|
||||
self.features = {}
|
||||
self.expand = expand
|
||||
|
||||
def append(self, obj):
|
||||
self.features[-1].append(obj)
|
||||
|
@ -95,6 +106,23 @@ class LogBDDCucumberJSON(object):
|
|||
for tag in item["tags"]
|
||||
]
|
||||
|
||||
def _format_name(self, name, keys, values):
|
||||
for param, value in zip(keys, values):
|
||||
name = name.replace('<{}>'.format(param), value)
|
||||
return name
|
||||
|
||||
def _format_step_name(self, report, step):
|
||||
examples = report.scenario["examples"]
|
||||
if len(examples) == 0:
|
||||
return step["name"]
|
||||
|
||||
# we take the keys from the first "examples", but in each table, the keys should
|
||||
# be the same anyway since all the variables need to be filled in.
|
||||
keys, values = examples[0]["rows"]
|
||||
row_index = examples[0]["row_index"]
|
||||
|
||||
return self._format_name(step["name"], keys, values[row_index])
|
||||
|
||||
def pytest_runtest_logreport(self, report):
|
||||
try:
|
||||
scenario = report.scenario
|
||||
|
@ -112,9 +140,17 @@ class LogBDDCucumberJSON(object):
|
|||
scenario['failed'] = True
|
||||
error_message = True
|
||||
|
||||
if self.expand:
|
||||
# XXX The format is already 'expanded' (scenario oultines -> scenarios),
|
||||
# but the step names were not filled in with parameters. To be backwards
|
||||
# compatible, do not fill in the step names unless explicitly asked for.
|
||||
step_name = self._format_step_name(report, step)
|
||||
else:
|
||||
step_name = step["name"]
|
||||
|
||||
return {
|
||||
"keyword": step['keyword'],
|
||||
"name": step['name'],
|
||||
"name": step_name,
|
||||
"line": step['line_number'],
|
||||
"match": {
|
||||
"location": "",
|
||||
|
|
|
@ -44,6 +44,16 @@ def test_step_trace(testdir):
|
|||
Scenario: Failing
|
||||
Given a passing step
|
||||
And a failing step
|
||||
|
||||
@scenario-outline-passing-tag
|
||||
Scenario: Passing outline
|
||||
Given type <type> and value <value>
|
||||
|
||||
Examples: example1
|
||||
| type | value |
|
||||
| str | hello |
|
||||
| int | 42 |
|
||||
| float | 1.0 |
|
||||
"""))
|
||||
testdir.makepyfile(textwrap.dedent("""
|
||||
import pytest
|
||||
|
@ -61,6 +71,10 @@ def test_step_trace(testdir):
|
|||
def a_failing_step():
|
||||
raise Exception('Error')
|
||||
|
||||
@given('type <type> and value <value>')
|
||||
def type_type_and_value_value():
|
||||
return 'pass'
|
||||
|
||||
@scenario('test.feature', 'Passing')
|
||||
def test_passing():
|
||||
pass
|
||||
|
@ -68,6 +82,10 @@ def test_step_trace(testdir):
|
|||
@scenario('test.feature', 'Failing')
|
||||
def test_failing():
|
||||
pass
|
||||
|
||||
@scenario('test.feature', 'Passing outline')
|
||||
def test_passing_outline():
|
||||
pass
|
||||
"""))
|
||||
result, jsonobject = runandparse(testdir)
|
||||
assert result.ret
|
||||
|
@ -156,6 +174,84 @@ def test_step_trace(testdir):
|
|||
}
|
||||
],
|
||||
"type": "scenario"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"keyword": "Scenario",
|
||||
"tags": [
|
||||
{
|
||||
"line": 14,
|
||||
"name": "scenario-outline-passing-tag"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"line": 16,
|
||||
"match": {"location": ""},
|
||||
"result": {
|
||||
"status": "passed",
|
||||
"duration": equals_any(int)
|
||||
},
|
||||
"keyword": "Given",
|
||||
"name": "type <type> and value <value>"
|
||||
}
|
||||
],
|
||||
"line": 15,
|
||||
"type": "scenario",
|
||||
"id": "test_passing_outline[str-hello]",
|
||||
"name": "Passing outline"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"keyword": "Scenario",
|
||||
"tags": [
|
||||
{
|
||||
"line": 14,
|
||||
"name": "scenario-outline-passing-tag"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"line": 16,
|
||||
"match": {"location": ""},
|
||||
"result": {
|
||||
"status": "passed",
|
||||
"duration": equals_any(int)
|
||||
},
|
||||
"keyword": "Given",
|
||||
"name": "type <type> and value <value>"
|
||||
}
|
||||
],
|
||||
"line": 15,
|
||||
"type": "scenario",
|
||||
"id": "test_passing_outline[int-42]",
|
||||
"name": "Passing outline"
|
||||
},
|
||||
{
|
||||
"description": "",
|
||||
"keyword": "Scenario",
|
||||
"tags": [
|
||||
{
|
||||
"line": 14,
|
||||
"name": "scenario-outline-passing-tag"
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"line": 16,
|
||||
"match": {"location": ""},
|
||||
"result": {
|
||||
"status": "passed",
|
||||
"duration": equals_any(int)
|
||||
},
|
||||
"keyword": "Given",
|
||||
"name": "type <type> and value <value>"
|
||||
}
|
||||
],
|
||||
"line": 15,
|
||||
"type": "scenario",
|
||||
"id": "test_passing_outline[float-1.0]",
|
||||
"name": "Passing outline"
|
||||
}
|
||||
],
|
||||
"id": os.path.join("test_step_trace0", "test.feature"),
|
||||
|
@ -173,3 +269,39 @@ def test_step_trace(testdir):
|
|||
]
|
||||
|
||||
assert jsonobject == expected
|
||||
|
||||
|
||||
def test_step_trace_with_expand_option(testdir):
|
||||
"""Test step trace."""
|
||||
testdir.makefile('.feature', test=textwrap.dedent("""
|
||||
@feature-tag
|
||||
Feature: One scenario outline, expanded to multiple scenarios
|
||||
|
||||
@scenario-outline-passing-tag
|
||||
Scenario: Passing outline
|
||||
Given type <type> and value <value>
|
||||
|
||||
Examples: example1
|
||||
| type | value |
|
||||
| str | hello |
|
||||
| int | 42 |
|
||||
| float | 1.0 |
|
||||
"""))
|
||||
testdir.makepyfile(textwrap.dedent("""
|
||||
import pytest
|
||||
from pytest_bdd import given, scenario
|
||||
|
||||
@given('type <type> and value <value>')
|
||||
def type_type_and_value_value():
|
||||
return 'pass'
|
||||
|
||||
@scenario('test.feature', 'Passing outline')
|
||||
def test_passing_outline():
|
||||
pass
|
||||
"""))
|
||||
result, jsonobject = runandparse(testdir, '--cucumber-json-expand')
|
||||
assert result.ret == 0
|
||||
|
||||
assert jsonobject[0]["elements"][0]["steps"][0]["name"] == "type str and value hello"
|
||||
assert jsonobject[0]["elements"][1]["steps"][0]["name"] == "type int and value 42"
|
||||
assert jsonobject[0]["elements"][2]["steps"][0]["name"] == "type float and value 1.0"
|
||||
|
|
Loading…
Reference in New Issue