Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print step that failed in verbosity=2 #311

Merged
merged 4 commits into from Jan 14, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lettuce/core.py
Expand Up @@ -144,7 +144,7 @@ def __call__(self, *args, **kw):
self.step.passed = True
except Exception, e:
self.step.failed = True
self.step.why = ReasonToFail(e)
self.step.why = ReasonToFail(self.step, e)
raise

return ret
Expand Down
3 changes: 2 additions & 1 deletion lettuce/exceptions.py
Expand Up @@ -35,7 +35,8 @@ class ReasonToFail(object):
AssertionError raised within a step definition. With these data
lettuce show detailed traceback to user in a nice representation.
"""
def __init__(self, exc):
def __init__(self, step, exc):
self.step = step
self.exception = exc
if isinstance(exc.message, unicode):
self.cause = unicode(exc)
Expand Down
80 changes: 19 additions & 61 deletions lettuce/plugins/dots.py
Expand Up @@ -16,76 +16,34 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import sys
from lettuce import core
from lettuce.terrain import after
from lettuce.terrain import before
from lettuce.plugins.reporter import Reporter

failed_scenarios = []
scenarios_and_its_fails = {}
class DotReporter(Reporter):
def print_scenario_ran(self, scenario):
if scenario.passed:
self.wrt(".")
elif scenario.failed:
reason = self.scenarios_and_its_fails[scenario]
if isinstance(reason.exception, AssertionError):
self.wrt("F")
else:
self.wrt("E")

reporter = DotReporter()

def wrt(what):
if isinstance(what, unicode):
what = what.encode('utf-8')
sys.stdout.write(what)


@after.each_step
def print_scenario_ran(step):
if not step.failed:
wrt(".")
elif step.failed:
if step.scenario not in failed_scenarios:
scenarios_and_its_fails[step.scenario] = step.why
failed_scenarios.append(step.scenario)

if isinstance(step.why.exception, AssertionError):
wrt("F")
else:
wrt("E")


@after.all
def print_end(total):
if total.scenarios_passed < total.scenarios_ran:
wrt("\n")
wrt("\n")
for scenario in failed_scenarios:
reason = scenarios_and_its_fails[scenario]
wrt(reason.traceback)

wrt("\n")
word = total.features_ran > 1 and "features" or "feature"
wrt("%d %s (%d passed)\n" % (
total.features_ran,
word,
total.features_passed))

word = total.scenarios_ran > 1 and "scenarios" or "scenario"
wrt("%d %s (%d passed)\n" % (
total.scenarios_ran,
word,
total.scenarios_passed))

steps_details = []
for kind in "failed", "skipped", "undefined":
attr = 'steps_%s' % kind
stotal = getattr(total, attr)
if stotal:
steps_details.append("%d %s" % (stotal, kind))

steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step"
wrt("%d %s (%s)\n" % (
total.steps,
word,
", ".join(steps_details)))
before.each_scenario(reporter.print_scenario_running)
after.each_scenario(reporter.print_scenario_ran)
after.each_step(reporter.store_failed_step)
after.all(reporter.print_end)


def print_no_features_found(where):
where = core.fs.relpath(where)
if not where.startswith(os.sep):
where = '.%s%s' % (os.sep, where)

wrt('Oops!\n')
wrt('could not find features at %s\n' % where)
reporter.wrt('Oops!\n')
reporter.wrt('could not find features at %s\n' % where)
59 changes: 59 additions & 0 deletions lettuce/plugins/reporter.py
@@ -0,0 +1,59 @@
import sys

class Reporter(object):
def __init__(self):
self.failed_scenarios = []
self.scenarios_and_its_fails = {}

def wrt(self, what):
if isinstance(what, unicode):
what = what.encode('utf-8')
sys.stdout.write(what)

def store_failed_step(self, step):
if step.failed and step.scenario not in self.failed_scenarios:
self.scenarios_and_its_fails[step.scenario] = step.why
self.failed_scenarios.append(step.scenario)

def print_scenario_running(self, scenario):
pass

def print_scenario_ran(self, scenario):
pass

def print_end(self, total):
if total.scenarios_passed < total.scenarios_ran:
self.wrt("\n")
self.wrt("\n")
for scenario in self.failed_scenarios:
reason = self.scenarios_and_its_fails[scenario]
self.wrt(str(reason.step))
self.wrt("\n")
self.wrt(reason.traceback)

self.wrt("\n")
word = total.features_ran > 1 and "features" or "feature"
self.wrt("%d %s (%d passed)\n" % (
total.features_ran,
word,
total.features_passed))

word = total.scenarios_ran > 1 and "scenarios" or "scenario"
self.wrt("%d %s (%d passed)\n" % (
total.scenarios_ran,
word,
total.scenarios_passed))

steps_details = []
for kind in "failed", "skipped", "undefined":
attr = 'steps_%s' % kind
stotal = getattr(total, attr)
if stotal:
steps_details.append("%d %s" % (stotal, kind))

steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step"
self.wrt("%d %s (%s)\n" % (
total.steps,
word,
", ".join(steps_details)))
86 changes: 21 additions & 65 deletions lettuce/plugins/scenario_names.py
Expand Up @@ -16,82 +16,38 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import sys
from lettuce import core
from lettuce.terrain import after
from lettuce.terrain import before
from lettuce.plugins.reporter import Reporter

failed_scenarios = []
scenarios_and_its_fails = {}
class NameReporter(Reporter):
def print_scenario_running(self, scenario):
self.wrt('%s ... ' % scenario.name)

def print_scenario_ran(self, scenario):
if scenario.passed:
self.wrt("OK")
elif scenario.failed:
reason = self.scenarios_and_its_fails[scenario]
if isinstance(reason.exception, AssertionError):
self.wrt("FAILED")
else:
self.wrt("ERROR")
self.wrt("\n")

def wrt(what):
if isinstance(what, unicode):
what = what.encode('utf-8')
sys.stdout.write(what)
reporter = NameReporter()


@before.each_scenario
def print_scenario_running(scenario):
wrt('%s ... ' % scenario.name)


@after.each_scenario
def print_scenario_ran(scenario):
if scenario.passed:
print "OK"
elif scenario.failed:
reason = scenarios_and_its_fails[scenario]
if isinstance(reason.exception, AssertionError):
print "FAILED"
else:
print "ERROR"


@after.each_step
def save_step_failed(step):
if step.failed and step.scenario not in failed_scenarios:
scenarios_and_its_fails[step.scenario] = step.why
failed_scenarios.append(step.scenario)


@after.all
def print_end(total):
if total.scenarios_passed < total.scenarios_ran:
print # just a line to separate things here
for scenario in failed_scenarios:
reason = scenarios_and_its_fails[scenario]
wrt(reason.traceback)

wrt("\n")
word = total.features_ran > 1 and "features" or "feature"
wrt("%d %s (%d passed)\n" % (
total.features_ran,
word,
total.features_passed))

word = total.scenarios_ran > 1 and "scenarios" or "scenario"
wrt("%d %s (%d passed)\n" % (
total.scenarios_ran,
word,
total.scenarios_passed))

steps_details = []
for kind in "failed", "skipped", "undefined":
attr = 'steps_%s' % kind
stotal = getattr(total, attr)
if stotal:
steps_details.append("%d %s" % (stotal, kind))

steps_details.append("%d passed" % total.steps_passed)
word = total.steps > 1 and "steps" or "step"
wrt("%d %s (%s)\n" % (total.steps, word, ", ".join(steps_details)))
before.each_scenario(reporter.print_scenario_running)
after.each_scenario(reporter.print_scenario_ran)
after.each_step(reporter.store_failed_step)
after.all(reporter.print_end)


def print_no_features_found(where):
where = core.fs.relpath(where)
if not where.startswith(os.sep):
where = '.%s%s' % (os.sep, where)

wrt('Oops!\n')
wrt('could not find features at %s\n' % where)
reporter.wrt('Oops!\n')
reporter.wrt('could not find features at %s\n' % where)
28 changes: 17 additions & 11 deletions tests/functional/test_runner.py
Expand Up @@ -908,6 +908,8 @@ def test_output_level_2_fail():
assert_stdout_lines_with_traceback(
"See it fail ... FAILED\n"
"\n"
"\n"
"<Step: \"And this one fails\">\n"
"Traceback (most recent call last):\n"
' File "%(lettuce_core_file)s", line %(call_line)d, in __call__\n'
" ret = self.function(self.step, *args, **kw)\n"
Expand Down Expand Up @@ -936,6 +938,8 @@ def test_output_level_2_error():
"It should pass ... OK\n"
"It should raise an exception different of AssertionError ... ERROR\n"
"\n"
"\n"
"<Step: \"Given my step that blows a exception\">\n"
"Traceback (most recent call last):\n"
' File "%(lettuce_core_file)s", line %(call_line)d, in __call__\n'
" ret = self.function(self.step, *args, **kw)\n"
Expand Down Expand Up @@ -977,8 +981,9 @@ def test_output_level_1_fail():
runner.run()

assert_stdout_lines_with_traceback(
".F...\n"
"F\n"
"\n"
"<Step: \"And this one fails\">\n"
"Traceback (most recent call last):\n"
' File "%(lettuce_core_file)s", line %(call_line)d, in __call__\n'
" ret = self.function(self.step, *args, **kw)\n"
Expand Down Expand Up @@ -1006,6 +1011,7 @@ def test_output_level_1_error():
assert_stdout_lines_with_traceback(
".E\n"
"\n"
"<Step: \"Given my step that blows a exception\">\n"
"Traceback (most recent call last):\n"
' File "%(lettuce_core_file)s", line %(call_line)d, in __call__\n'
" ret = self.function(self.step, *args, **kw)\n"
Expand Down Expand Up @@ -1060,7 +1066,7 @@ def string_lenth_calc(step):
runner.run()

assert_stdout_lines(
"...."
"."
"\n"
"1 feature (1 passed)\n"
"1 scenario (1 passed)\n"
Expand Down Expand Up @@ -1098,7 +1104,7 @@ def time_passed_1_min(step):
runner.run()

assert_stdout_lines(
".."
"."
"\n"
"1 feature (1 passed)\n"
"1 scenario (1 passed)\n"
Expand Down Expand Up @@ -1142,7 +1148,7 @@ def multiply_and_verify(step, name, times, expected):
runner.run()

assert_stdout_lines(
"........."
".."
"\n"
"1 feature (1 passed)\n"
"2 scenarios (2 passed)\n"
Expand Down Expand Up @@ -1189,7 +1195,7 @@ def multiply_and_verify(step, name, times, expected):
runner.run()

assert_stdout_lines(
"........."
".."
"\n"
"1 feature (1 passed)\n"
"2 scenarios (2 passed)\n"
Expand Down Expand Up @@ -1229,10 +1235,10 @@ def just_pass(step, *args):
' I want to automate its test # tests/functional/bg_features/simple/simple.feature:4\n'
'\n'
' Background:\n'
' Given the variable "X" holds 2 # tests/functional/test_runner.py:1215\n'
' Given the variable "X" holds 2 # tests/functional/test_runner.py:1221\n'
'\n'
' Scenario: multiplication changing the value # tests/functional/bg_features/simple/simple.feature:9\n'
' Given the variable "X" is equal to 2 # tests/functional/test_runner.py:1215\n'
' Given the variable "X" is equal to 2 # tests/functional/test_runner.py:1221\n'
'\n'
'1 feature (1 passed)\n'
'1 scenario (1 passed)\n'
Expand Down Expand Up @@ -1264,12 +1270,12 @@ def just_pass(step, *args):
'\033[1;37m I want to automate its test \033[1;30m# tests/functional/bg_features/simple/simple.feature:4\033[0m\n'
'\n'
'\033[1;37m Background:\033[0m\n'
'\033[1;30m Given the variable "X" holds 2 \033[1;30m# tests/functional/test_runner.py:1250\033[0m\n'
'\033[A\033[1;32m Given the variable "X" holds 2 \033[1;30m# tests/functional/test_runner.py:1250\033[0m\n'
'\033[1;30m Given the variable "X" holds 2 \033[1;30m# tests/functional/test_runner.py:1256\033[0m\n'
'\033[A\033[1;32m Given the variable "X" holds 2 \033[1;30m# tests/functional/test_runner.py:1256\033[0m\n'
'\n'
'\033[1;37m Scenario: multiplication changing the value \033[1;30m# tests/functional/bg_features/simple/simple.feature:9\033[0m\n'
'\033[1;30m Given the variable "X" is equal to 2 \033[1;30m# tests/functional/test_runner.py:1250\033[0m\n'
'\033[A\033[1;32m Given the variable "X" is equal to 2 \033[1;30m# tests/functional/test_runner.py:1250\033[0m\n'
'\033[1;30m Given the variable "X" is equal to 2 \033[1;30m# tests/functional/test_runner.py:1256\033[0m\n'
'\033[A\033[1;32m Given the variable "X" is equal to 2 \033[1;30m# tests/functional/test_runner.py:1256\033[0m\n'
'\n'
'\033[1;37m1 feature (\033[1;32m1 passed\033[1;37m)\033[0m\n'
'\033[1;37m1 scenario (\033[1;32m1 passed\033[1;37m)\033[0m\n'
Expand Down