[Baypiggies] Unittest magic -- how to get into that exception handler?

Bob Van Zant bob at eventbrite.com
Tue Mar 29 02:30:20 CEST 2011


Hey Wai Yip!
I think a Mock is exactly what you want. In fact I think your marker
proposal is basically a specialized form of monkey patching in mocks
that return specified values. The mocking library I reference below
does a good job of this.

In the example you've given it's sort of a pain in the butt to use a
mock because you have to monkey patch my_processing in order to mock
it out. If your function took the processing function as an argument
you could very easily:

def super_bogus_processing():
    return None
def bad_len_processing():
    return ['list of length 1']

def myfunc(processor_func=my_processing):
    result = processor_func()
    ...

self.assertRaises(RuntimeException, myfunc, super_bogus_processing)
self.assertRaises(RuntimeException, myfunc, bad_len_processing)


super_bogus_processing exploits an unhandled exception in your sample.
len of None raises TypeError.

I think in this particular example, with code that's designed for it,
mocking is really the way to go.

A shameless plug for a mocking framework named ditto:
http://code.google.com/p/ditto/. This one does a good job of Mocking
existing classes and modeling their interface at runtime rather than
creating some arbitrary interface that never fails when an unknown
method or attribute is accessed.

-Bob



On Mon, Mar 28, 2011 at 4:52 PM, Tung Wai Yip <tungwaiyip at yahoo.com> wrote:
> In my code there is a lot of result checking and exception handling code,
> e.g.
>
>
> def myfunc():
>    result = my_processing()
>    if len(result) != 2:
>        raise RuntimeException("Result not right, len=%s" % len(result))
>
>    # verified result is a sequence of len 2 as documented
>    ...
>
>
> Normally, this exception handling code is never run because the error
> condition does not happen. But it is never a good idea to ship code that has
> never been run. Many times I get embarrassing problem like the error
> message's formating string does not match the parameter or just typo.
> Instead of helping, the checking code creates further complication. This is
> even more problematic for a dynamic language like Python with less compile
> time checking.
>
> So I really want to get the code to run, using automatic unit testing or
> other means. The conventional way to handle this is to create mock object
> for things like `my_processing`. I'm not a big fan of mock object. First of
> all I have a lot of those situations and thus a lot of things to mock. I
> don't like the extra layer of indirection and I find mock object are often
> very hackish. Plus it creates a lot of maintenance issue. When I refactor
> the logic, I also have to take care of the mock object or they will be the
> first to break.
>
> I have some idea that is to use annotation to inject code to push the
> execution into some path. That requires some language magic I wonder if it
> is feasible. For example, I'm thinking of introducing some marker like
> @unittest_magic.
>
>
> def myfunc():
>    result = my_processing()
>    @unittest_magic(name="my_processing_result_validation")
>    if len(result) != 2:
>        raise RuntimeException("Result not right, len=%s" % len(result))
>
>    # verified result is a sequence of len 2
>    ...
>
>
> Then elsewhere I can invoke it and specified it to take on any arbitrary
> variable value when the execution has reached the point by:
>
>  run_test(myfunc, "my_processing_result_validation", result=["dummy"])
>
>
> I think this is fairly lightweight and localized magic compares to mock
> object. What do you think? Is it feasible? Or do you have other strategy to
> handle those situation?
>
>
> Wai Yip
> _______________________________________________
> Baypiggies mailing list
> Baypiggies at python.org
> To change your subscription options or unsubscribe:
> http://mail.python.org/mailman/listinfo/baypiggies
>


More information about the Baypiggies mailing list