[TIP] RFF: Article on python test design pattern - DAI

Gary Bernhardt gary.bernhardt at gmail.com
Tue Dec 8 09:58:54 PST 2009


I liked this article! I'm glad to see discussion of advanced testing,
dependency injection, and *actual* unit testing in the Python world.
Please keep it up! :)

One thing that I'd point out is that you *are* mocking when you do
this; it's just that Python allows you to hand-roll mocks very easily.
This certainly doesn't change your objections to mocking libraries,
but it might be useful to point out so that people realize the
parallels.

Here's a little critique of the test from the point of view of a
stub/mock library author. :) You write:

class TestUrl(unittest.TestCase):
    def test_build_url(self):
        def myurlopen(url):
            self.assertEqual('http://example.com/testpath.json', url)
        rres = RemoteResource('testpath.json', myurlopen)

My main complaint is that the assert isn't the last line; I can't see,
at a glance, what's being tested. In other words, the test doesn't
follow arrange-act-assert. Consistent use of AAA test organization
makes it easier to scan tests quickly, which matters a lot with a
large suite!

Here's how I'd write it using Dingus, which I'm biased toward because
I wrote it. :)

class TestUrl:
    def test_build_url(self):
        urlopen = Dingus()
        RemoteResource('testpath.json', urlopen)
        assert urlopen.calls('()', 'http://example.com/testpath.json')

No SUT changes are needed, and it's the same number of lines, but it's
more expressive in my opinion, mostly because it follows the
arrange-act-assert pattern. There's exactly one line per step: arrange
the collaborators, perform the action under test, then assert on the
collaboration.

This has the added benefit of preventing the mistake that Jean-Paul is
worried about: since the assertion is now inside the test itself,
there's no chance of the SUT preventing it from running.

Dingus also has a magical feature that can remove the need for
injection entirely by auto-stubbing the SUT's module namespace.
However, as you can probably imagine, it tends to encourage hard
coupling. If you're interested, I did a short screencast [1] about it.
Unfortunately, I don't give any warnings about its dangers, because I
hadn't realized them at the time.

I'd recommend taking a look at a lightweight mock/stub library like
Dingus or Michael Foord's Mock, which is the project that Dingus was
originally forked from. They're both very short – Dingus is about 250
lines, including the magical isolation features, and Mock was about
the same length last I checked. They use a bit of Python magic (or a
lot in DingusTestCase's case), but in a consistent and predictable
way. They're both designed to minimize ceremony. These aren't your
father's mock libraries. :)

[1] http://blog.extracheese.org/2009/04/dingus-screencast-a-mock-stub-library-with-automatic-isolation.html

On Mon, Dec 7, 2009 at 8:07 PM, Aaron Maxwell <amax at redsymbol.net> wrote:
> (that's Request For Feedback)
>
> Hi everyone,
>
> Here is a draft article about a useful design pattern for Python code tests:
>
> http://redsymbol.net/articles/deep-assertion-injection/
>
> I am surely not the first to use this idiom, but I *might* be the first to
> document it this well, and to give it a name.  Have you seen this before?
> Constructive feedback is appreciated.
>
> Thanks in advance for your comments.  Either reply here on the list, or by
> email to amax at redsymbol.net.
>
> Cheers,
> Aaron
>
> --
> Aaron Maxwell
> http://redsymbol.net/
>
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
> http://lists.idyll.org/listinfo/testing-in-python
>



-- 
Gary
http://blog.extracheese.org



More information about the testing-in-python mailing list