سؤال

I have written an assert_raised context manager for testing which checks that as expected exception is raised, and if not raises an AssertionError. I have also written a doctest to test this, but the doctest keeps failing and I'm not entirely sure why. Here is the doctest:

>>> for e in [TypeError, ValueError, KeyError]:
...     with assert_raised(TypeError, ValueError):
...         print('Raising {}...'.format(e.__name__))
...         raise e
Raising TypeError...
Raising ValueError...
Raising KeyError...
Traceback (most recent call last):
    ...
AssertionError: Got 'KeyError', expected 'TypeError, ValueError'

And the actual exception raised is:

Traceback (most recent call last):
  File "<doctest dtlibs.mock.assert_raised[3]>", line 4, in <module>
    raise e
KeyError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python32\lib\doctest.py", line 1253, in __run
    compileflags, 1), test.globs)
  File "<doctest dtlibs.mock.assert_raised[3]>", line 4, in <module>
    raise e
  File "G:\Projects\Programming\dt-tools\dtlibs\dtlibs\mock.py", line 274, in __exit__
    raise self._exception(exc_type.__name__)
AssertionError: Got 'KeyError', expected 'TypeError, ValueError'

I don't think that the implementation is important, but here it is in case I'm doing something wrong there (without the doctest):

class assert_raised:

    def __init__(self, *exceptions):
        self.exceptions = exceptions

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            raise self._exception('None')
        elif self.exceptions and exc_type not in self.exceptions:
            raise self._exception(exc_type.__name__)
        return True

    def _exception(self, got):
        if len(self.exceptions) == 0:
            expected = 'an exception'
        else:
            expected = ', '.join(e.__name__ for e in self.exceptions)
        msg = "Got '{}', expected '{}'".format(got, expected)
        return AssertionError(msg)
هل كانت مفيدة؟

المحلول

Doctests has special code for dealing with raising exceptions. As such it will recognize when the output is an exception. For it to recognize this, the expected output must start with the word Traceback. Your expected output does not start with this, and therefore, the doctest will not recognize the output as expecting an exception, and hence, it will not expect an exception, so when the exception comes, it will fail.

You can fix this in three ways:

  1. Get rid of the Raising XXXError... part of the output. [lazy]

  2. Implement special output filters for doctest which enables the doctest to ignore the Raising XXXError...-part [complicated]

  3. Stop using doctests for other things than testing documentation. [correct]

The above code is clearly not example code of how to use this context manager, it's code that tests that the context manager works. Such tests should never be doctests. Doctests are painful and limited, and should only be used to test documentation.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top