Skip to content

Commit 0703e1d

Browse files
XonqNoppdnozay
authored andcommitted
Fix subtests (#178)
* [result] trying to fix subtest failures * [result] adding missing methods to add expected failure and unexpected success * [result] trying to add missing subdescription * [tests] adding missing test for subtest error
1 parent 8417e47 commit 0703e1d

File tree

2 files changed

+85
-4
lines changed

2 files changed

+85
-4
lines changed

tests/testsuite.py

+29
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ def test_subTest_fail(self):
147147
with self.subTest(i=i):
148148
self.fail('this is a subtest.')
149149

150+
def test_subTest_error(self):
151+
for i in range(2):
152+
with self.subTest(i=i):
153+
raise Exception('this is a subtest')
154+
150155
class DummyErrorInCallTest(unittest.TestCase):
151156

152157
def __call__(self, result):
@@ -416,6 +421,30 @@ def test_unittest_subTest_fail(self):
416421
br'(XMLTestRunnerTestCase\.)?DummySubTest" '
417422
br'name="test_subTest_fail \(i=1\)"')
418423

424+
@unittest.skipIf(not hasattr(unittest.TestCase, 'subTest'),
425+
'unittest.TestCase.subTest not present.')
426+
def test_unittest_subTest_error(self):
427+
# test for issue #155
428+
outdir = BytesIO()
429+
runner = xmlrunner.XMLTestRunner(
430+
stream=self.stream, output=outdir, verbosity=self.verbosity,
431+
**self.runner_kwargs)
432+
suite = unittest.TestSuite()
433+
suite.addTest(self.DummySubTest('test_subTest_error'))
434+
runner.run(suite)
435+
outdir.seek(0)
436+
output = outdir.read()
437+
self.assertRegexpMatches(
438+
output,
439+
br'<testcase classname="tests\.testsuite\.'
440+
br'(XMLTestRunnerTestCase\.)?DummySubTest" '
441+
br'name="test_subTest_error \(i=0\)"')
442+
self.assertRegexpMatches(
443+
output,
444+
br'<testcase classname="tests\.testsuite\.'
445+
br'(XMLTestRunnerTestCase\.)?DummySubTest" '
446+
br'name="test_subTest_error \(i=1\)"')
447+
419448
@unittest.skipIf(not hasattr(unittest.TestCase, 'subTest'),
420449
'unittest.TestCase.subTest not present.')
421450
def test_unittest_subTest_pass(self):

xmlrunner/result.py

+56-4
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ def __init__(self, test_result, test_method, outcome=SUCCESS, err=None, subTest=
156156

157157
self.test_name = testcase_name(test_method)
158158
self.test_id = test_method.id()
159+
self.subDescription = None
159160
if subTest:
160161
self.test_id = subTest.id()
162+
self.subDescription = subTest._subDescription()
161163

162164
def id(self):
163165
return self.test_id
@@ -174,7 +176,12 @@ def get_description(self):
174176
"""
175177
Return a text representation of the test method.
176178
"""
177-
return self.test_description
179+
description = self.test_description
180+
181+
if self.subDescription is not None:
182+
description += ' ' + self.subDescription
183+
184+
return description
178185

179186
def get_error_info(self):
180187
"""
@@ -337,14 +344,29 @@ def addSubTest(self, testcase, test, err):
337344
Called when a subTest method raises an error.
338345
"""
339346
if err is not None:
347+
348+
errorText = None
349+
errorValue = None
350+
errorList = None
351+
if issubclass(err[0], test.failureException):
352+
errorText = 'FAIL'
353+
errorValue = self.infoclass.FAILURE
354+
errorList = self.failures
355+
356+
else:
357+
errorText = 'ERROR'
358+
errorValue = self.infoclass.ERROR
359+
errorList = self.errors
360+
340361
self._save_output_data()
362+
341363
testinfo = self.infoclass(
342-
self, testcase, self.infoclass.ERROR, err, subTest=test)
343-
self.errors.append((
364+
self, testcase, errorValue, err, subTest=test)
365+
errorList.append((
344366
testinfo,
345367
self._exc_info_to_string(err, testcase)
346368
))
347-
self._prepare_callback(testinfo, [], 'ERROR', 'E')
369+
self._prepare_callback(testinfo, [], errorText, errorText[0])
348370

349371
def addSkip(self, test, reason):
350372
"""
@@ -356,6 +378,36 @@ def addSkip(self, test, reason):
356378
self.skipped.append((testinfo, reason))
357379
self._prepare_callback(testinfo, [], 'SKIP', 'S')
358380

381+
def addExpectedFailure(self, test, err):
382+
"""
383+
Missing in xmlrunner, copy-pasted from xmlrunner addError.
384+
"""
385+
self._save_output_data()
386+
387+
testinfo = self.infoclass(self, test, self.infoclass.ERROR, err)
388+
testinfo.test_exception_name = 'ExpectedFailure'
389+
testinfo.test_exception_message = 'EXPECTED FAILURE: {}'.format(testinfo.test_exception_message)
390+
391+
self.expectedFailures.append((testinfo, self._exc_info_to_string(err, test)))
392+
self._prepare_callback(testinfo, [], 'EXPECTED FAILURE', 'X')
393+
394+
@failfast
395+
def addUnexpectedSuccess(self, test):
396+
"""
397+
Missing in xmlrunner, copy-pasted from xmlrunner addSuccess.
398+
"""
399+
self._save_output_data()
400+
401+
testinfo = self.infoclass(self, test) # do not set outcome here because it will need exception
402+
testinfo.outcome = self.infoclass.ERROR
403+
# But since we want to have error outcome, we need to provide additional fields:
404+
testinfo.test_exception_name = 'UnexpectedSuccess'
405+
testinfo.test_exception_message = ('UNEXPECTED SUCCESS: This test was marked as expected failure but passed, '
406+
'please review it')
407+
408+
self.unexpectedSuccesses.append(testinfo)
409+
self._prepare_callback(testinfo, [], 'UNEXPECTED SUCCESS', 'U')
410+
359411
def printErrorList(self, flavour, errors):
360412
"""
361413
Writes information about the FAIL or ERROR to the stream.

0 commit comments

Comments
 (0)