[TIP] [SOLUTION] Asserting behaviour of exception tracebacks

Ben Finney bignose+hates-spam at benfinney.id.au
Thu Jan 31 22:35:36 PST 2008


Ben Finney <bignose+hates-spam at benfinney.id.au> writes:

> Michael Foord <fuzzyman at voidspace.org.uk> writes:
> > Well - if you find a *use-case* for the traceback object - then
> > you can test against that use case.
> 
> This is the right way to think about the problem, thanks. I'll have
> to consider exactly what it is I want to assert about the behaviour,
> in terms of how the code is used.

Here's the solution I came up with. Likely others would have arrived
at something different to some degree.

The relevant unit test looks similar to this::

    import sys
    import minimock
    from custom_test_framework import TestCase

    import module_under_test

    class Test_Foo(TestCase):
        """ Test cases for the Foo class """

        def tearDown(self):
            """ Tear down test fixtures """
            minimock.restore()

        # ...

        def test_frobnicate_raises_traceback_to_original_failure(self):
            """ frobnicate() should raise traceback leading to the failure """

            test_error = StandardError("Uh-oh!")
            mock_inner_func = Mock(raises=test_error)
            minimock.mock("module_under_test.something_called_by_frobnicate",
                mock_obj=mock_inner_func)

            try:
                module_under_test.frobnicate()
            except module_under_test.ModuleSpecificError:
                exc_type, exc, exc_traceback = sys.exc_info()
            else:
                raise self.failureException("Failed to raise ModuleSpecificError")

            self.failUnlessFunctionInTraceback(
                exc_traceback, mock_inner_func.__call__)

The 'failUnlessFunctionInTraceback' was implemented in-line in the
test case, until I needed it elsewhere and decided to refactor. Then,
the 'custom_test_framework.TestCase' class grew a helper function::

    import unittest

    class TestCase(unittest.TestCase):
        # ...

        def failUnlessFunctionInTraceback(self, traceback, function):
            """ Fail if the function 'function' is not in 'traceback' """
            func_in_traceback = False
            expect_code = function.func_code
            current_traceback = traceback
            while current_traceback is not None:
                if expect_code is current_traceback.tb_frame.f_code:
                    func_in_traceback = True
                    break
                current_traceback = current_traceback.tb_next

            if not func_in_traceback:
                msg = ("Traceback did not lead to original function"
                    " %(function)s"
                    ) % vars()
                raise self.failureException(msg)

        assertFunctionInTraceback = failUnlessFunctionInTraceback

Thanks to those who led me in the right direction.

-- 
 \      "An idea isn't responsible for the people who believe in it."  |
  `\                                      —Donald Robert Perry Marquis |
_o__)                                                                  |
Ben Finney




More information about the testing-in-python mailing list