Skip to content

Commit 0533015

Browse files
committed
Handle missing expected errors better
- Better error message for missing error in output compare. - Fail cell if an unrun cell with an expected error does not produce one.
1 parent 7c6cd71 commit 0533015

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

nbval/plugin.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -428,12 +428,22 @@ def compare_outputs(self, test, ref, skip_compare=None):
428428
test_keys = set(testing_outs.keys())
429429

430430
if ref_keys - test_keys:
431-
self.comparison_traceback.append(
432-
cc.FAIL
433-
+ "Missing output fields from running code: %s"
434-
% (ref_keys - test_keys)
435-
+ cc.ENDC
436-
)
431+
if ref_keys == {'evalue', 'ename'}:
432+
self.comparison_traceback.append(
433+
cc.FAIL
434+
+ "Expected error:\n %s: %r" % (
435+
'\n'.join(reference_outs['ename']),
436+
'\n'.join(reference_outs['evalue'])
437+
)
438+
+ cc.ENDC
439+
)
440+
else:
441+
self.comparison_traceback.append(
442+
cc.FAIL
443+
+ "Missing output fields from running code: %s"
444+
% (ref_keys - test_keys)
445+
+ cc.ENDC
446+
)
437447
return False
438448
elif test_keys - ref_keys:
439449
self.comparison_traceback.append(
@@ -582,6 +592,13 @@ def runtest(self):
582592
# TODO: Only store if comparing with nbdime, to save on memory usage
583593
self.test_outputs = outs
584594

595+
# Cells where the reference is not run, will not check outputs:
596+
unrun = self.cell.execution_count is None
597+
if unrun and self.cell.outputs:
598+
self.raise_cell_error('Unrun reference cell has outputs')
599+
600+
cell_has_error = False
601+
585602
# Now get the outputs from the iopub channel
586603
while True:
587604
# The iopub channel broadcasts a range of messages. We keep reading
@@ -692,6 +709,7 @@ def runtest(self):
692709
# cell execution. Therefore raise a cell error and pass the
693710
# traceback information.
694711
elif msg_type == 'error':
712+
cell_has_error = True
695713
# Store error in output first
696714
out['ename'] = reply['ename']
697715
out['evalue'] = reply['evalue']
@@ -700,9 +718,9 @@ def runtest(self):
700718
if not self.options['check_exception']:
701719
# Ensure we flush iopub before raising error
702720
try:
703-
self.parent.kernel.await_idle(msg_id, self.output_timeout)
721+
kernel.await_idle(msg_id, self.output_timeout)
704722
except Empty:
705-
self.stop()
723+
kernel.stop()
706724
raise RuntimeError('Timed out waiting for idle kernel!')
707725
traceback = '\n' + '\n'.join(reply['traceback'])
708726
if out['ename'] == 'KeyboardInterrupt' and self.parent.timed_out:
@@ -718,10 +736,11 @@ def runtest(self):
718736

719737
outs[:] = coalesce_streams(outs)
720738

721-
# Cells where the reference is not run, will not check outputs:
722-
unrun = self.cell.execution_count is None
723-
if unrun and self.cell.outputs:
724-
self.raise_cell_error('Unrun reference cell has outputs')
739+
if self.options['check_exception'] and unrun and not cell_has_error:
740+
# If unrun, we cannot rely on output comparison for checking errors
741+
self.raise_cell_error(
742+
"Expected error",
743+
"Expected cell to produce an error, but none was produced!")
725744

726745
# Compare if the outputs have the same number of lines
727746
# and throw an error if it fails

tests/test_expected_exceptions.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import os
2+
3+
import nbformat
4+
import pytest
5+
6+
from utils import build_nb
7+
8+
9+
pytest_plugins = "pytester"
10+
11+
12+
def test_unrun_raises(testdir):
13+
# This test uses the testdir fixture from pytester, which is useful for
14+
# testing pytest plugins. It writes a notebook to a temporary dir
15+
# and then runs pytest.
16+
17+
# Setup notebook to test:
18+
sources = [
19+
# In [1]:
20+
"raise ValueError('foo')",
21+
]
22+
# Build unrun notebook:
23+
nb = build_nb(sources, mark_run=False)
24+
25+
# Write notebook to test dir
26+
nbformat.write(nb, os.path.join(
27+
str(testdir.tmpdir), 'test_expcted_exceptions.ipynb'))
28+
29+
# Run tests
30+
result = testdir.inline_run('--nbval', '--current-env', '-s')
31+
reports = result.getreports('pytest_runtest_logreport')
32+
33+
# Setup and teardown of cells should have no issues:
34+
setup_teardown = [r for r in reports if r.when != 'call']
35+
for r in setup_teardown:
36+
assert r.passed
37+
38+
reports = [r for r in reports if r.when == 'call']
39+
40+
assert len(reports) == 1
41+
42+
# First cell should fail, unexpectedly
43+
assert reports[0].failed and not hasattr(reports[0], 'wasxfail')

0 commit comments

Comments
 (0)