Python automatic testing: mocking an imported module?

Discussion in 'Python' started by Silfheed, Mar 27, 2007.

  1. Silfheed

    Silfheed Guest

    Heyas

    So we have the following situation: we have a testee.py that we want
    to automatically test out and verifiy that it is worthy of being
    deployed. We want our tester.py to test the code for testee.py
    without changing the code for testee.py. testee.py has a module in it
    that we want to mock in some tests and in others use the real module.

    /foo.py: (real module)
    class bar:
    def __init__(self):
    "I am real"

    /foo_fake/foo.py: (fake module)
    class bar:
    def ___init__(self):
    "I am a banana"

    /testee.py:
    import foo
    foo.bar()

    /tester.py:
    from foo_fake import foo
    foo.bar() # prints I am a banana
    testee.py # also prints I am a banana
    import foo
    foo.bar() # prints I am real
    testee.py # also prints I am real


    This isnt working as we would like, does anyone have any tips on how
    to get something like this working?
     
    Silfheed, Mar 27, 2007
    #1
    1. Advertising

  2. Silfheed

    Chris Lasher Guest

    On Mar 27, 6:18 pm, "Silfheed" <> wrote:
    > Heyas
    >
    > So we have the following situation: we have a testee.py that we want
    > to automatically test out and verifiy that it is worthy of being
    > deployed. We want our tester.py to test the code for testee.py
    > without changing the code for testee.py. testee.py has a module in it
    > that we want to mock in some tests and in others use the real module.


    I think you mean "testee.py *imports* a module", rather than "has a
    module in it". Semantics but I was thrown off by this at first.

    Why not write tests for the module that testee.py imports (here called
    "foo.py")? If you write out the tests and foo.py passes to spec, then
    you should feel confident it will work correctly with testee.py. Never
    trust it; always play paranoid coder, but feel comfortable enough
    using it.

    After all, why should you feel comfortable using modules in the
    standard library? Nobody guarantees bugs don't still lurk in there
    (heck, c.l.p just found one in pdb today), however, they have unit
    tests out the wazzoo that demonstrate, to the best of our knowledge,
    the module works to spec. If that approach is good enough for the
    standard library, wouldn't it be good enough for you?

    Chris
     
    Chris Lasher, Mar 28, 2007
    #2
    1. Advertising

  3. Silfheed

    Ben Finney Guest

    "Silfheed" <> writes:

    > So we have the following situation: we have a testee.py that we want
    > to automatically test out and verifiy that it is worthy of being
    > deployed.


    This is sometimes called the "module under test". I wish there was a
    more succinct name in common usage.

    > We want our tester.py to test the code for testee.py
    > without changing the code for testee.py.


    This is called a "unit test" for the module.

    > testee.py has a module in it


    Looking at your example, testee.py *imports* a module; it doesn't
    contain another one.

    > that we want to mock in some tests and in othemodule.


    Excellent idea.


    ===== foo.py =====
    class Bar(object):
    def __init__(self):
    self.name = "bar"
    =====

    ===== dostuff.py =====
    import foo

    def get_bar():
    return foo.Bar()
    =====

    ===== test_dostuff.py =====
    import dostuff

    class Mock_foo_module(object):
    """ Mock object for foo module """

    class Bar(object):
    def __init__(self):
    self.name = "banana"

    def test_dostuff_get_bar_should_create_bar():
    """ The dostuff.get_bar function should create a new Bar """
    dostuff.foo = Mock_foo_module()
    test_bar = dostuff.get_bar()
    if not test_bar.name == "banana":
    raise AssertionError("test_bar.name should be banana")
    =====

    This is simply using an instance of a class (Mock_foo_module) to be a
    mock 'module' object. That mock module has the required 'Bar'
    attribute, bound to a class; so the 'dostuff' module's 'foo' attribute
    can be replaced with our mock module object for the purpose of the
    test.

    The mock module's Bar class behaves in an idiomatic way (setting the
    'name' attribute of its instance to a known value) that we use to
    check whether the 'dostuff' module actually used our mock module.

    This can be extended to mock any module interface you like, and then
    confirm that the module under test has actually used the module as
    expected.

    --
    \ "A politician is an animal which can sit on a fence and yet |
    `\ keep both ears to the ground." -- Henry L. Mencken |
    _o__) |
    Ben Finney
     
    Ben Finney, Mar 28, 2007
    #3
  4. Silfheed

    Peter Otten Guest

    Silfheed wrote:

    > So we have the following situation: we have a testee.py that we want
    > to automatically test out and verifiy that it is worthy of being
    > deployed. We want our tester.py to test the code for testee.py
    > without changing the code for testee.py. testee.py has a module in it
    > that we want to mock in some tests and in others use the real module.
    >
    > /foo.py: (real module)
    > class bar:
    > def __init__(self):
    > "I am real"
    >
    > /foo_fake/foo.py: (fake module)
    > class bar:
    > def ___init__(self):
    > "I am a banana"
    >
    > /testee.py:
    > import foo
    > foo.bar()
    >
    > /tester.py:
    > from foo_fake import foo
    > foo.bar() # prints I am a banana
    > testee.py # also prints I am a banana
    > import foo
    > foo.bar() # prints I am real
    > testee.py # also prints I am real
    >
    >
    > This isnt working as we would like, does anyone have any tips on how
    > to get something like this working?


    You can put foo_fake.foo into sys.modules as foo:

    import sys
    from foo_fake import foo

    assert "foo" not in sys.modules
    sys.modules["foo"] = foo

    foo.bar() # prints I am a banana
    import foo
    foo.bar() # prints I am a banana

    Peter
     
    Peter Otten, Mar 28, 2007
    #4
  5. Silfheed

    Mark T Guest

    "Silfheed" <> wrote in message
    news:...
    > Heyas
    >
    > So we have the following situation: we have a testee.py that we want
    > to automatically test out and verifiy that it is worthy of being
    > deployed. We want our tester.py to test the code for testee.py
    > without changing the code for testee.py. testee.py has a module in it
    > that we want to mock in some tests and in others use the real module.
    >
    > /foo.py: (real module)
    > class bar:
    > def __init__(self):
    > "I am real"
    >
    > /foo_fake/foo.py: (fake module)
    > class bar:
    > def ___init__(self):
    > "I am a banana"
    >
    > /testee.py:
    > import foo
    > foo.bar()
    >
    > /tester.py:
    > from foo_fake import foo
    > foo.bar() # prints I am a banana
    > testee.py # also prints I am a banana
    > import foo
    > foo.bar() # prints I am real
    > testee.py # also prints I am real
    >
    >
    > This isnt working as we would like, does anyone have any tips on how
    > to get something like this working?
    >


    If you add the foo_fake directory to the front of sys.path, "import foo"
    will import the foo.py in foo_fake directory before the one in the local
    directory.

    # unverified code
    import sys
    sys.path.append(0,'foo_fake') # add foo_fake to front of path
    import foo
    foo.bar()
    execfile('testee.py')
    sys.path.pop(0) # remove foo_fake
    reload(foo)
    foo.bar()
    execfile('testee.py')

    -Mark
     
    Mark T, Mar 28, 2007
    #5
  6. Silfheed

    Guest

    On Mar 27, 9:13 pm, "Chris Lasher" <> wrote:
    > On Mar 27, 6:18 pm, "Silfheed" <> wrote:
    >
    > > Heyas

    >
    > > So we have the following situation: we have a testee.py that we want
    > > to automatically test out and verifiy that it is worthy of being
    > > deployed. We want our tester.py to test the code for testee.py
    > > without changing the code for testee.py. testee.py has a module in it
    > > that we want to mock in some tests and in others use the real module.

    >
    > I think you mean "testee.py *imports* a module", rather than "has a
    > module in it". Semantics but I was thrown off by this at first.
    >
    > Why not write tests for the module that testee.py imports (here called
    > "foo.py")? If you write out the tests and foo.py passes to spec, then
    > you should feel confident it will work correctly with testee.py. Never
    > trust it; always play paranoid coder, but feel comfortable enough
    > using it.
    >
    > After all, why should you feel comfortable using modules in the
    > standard library? Nobody guarantees bugs don't still lurk in there
    > (heck, c.l.p just found one inpdbtoday),


    A slight correction. At present it looks like the bug is in Python,
    and its handling after setting the current frame's f_lineno when it
    refers to byte code offset 0, not pdb. And if you scan c.l.p for
    another oddity in pdb with respect to tracebacks, there is possibly
    another bug in Python (not strictly pdb).

    > however, they have unit
    > tests out the wazzoo that demonstrate, to the best of our knowledge,
    > the module works to spec.


    Actually, as far as I know pdb *doesn't* have any unit tests! (pydb
    does though ;-)

    But all this nit-picking aside, I do agree with what you have to say
    most whole heartedly. (And I'd say maybe a couple more unit tests for
    Python to cover these bugs are in order.)

    > If that approach is good enough for the
    > standard library, wouldn't it be good enough for you?
    >
    > Chris
     
    , Mar 29, 2007
    #6
  7. Silfheed

    Silfheed Guest

    Wow, that works great! Thanks all!

    On Mar 28, 12:02 am, Ben Finney <>
    wrote:
    > "Silfheed" <> writes:
    > ===== foo.py =====
    > class Bar(object):
    > def __init__(self):
    > self.name = "bar"
    > =====
    >
    > ===== dostuff.py =====
    > import foo
    >
    > def get_bar():
    > return foo.Bar()
    > =====
    >
    > ===== test_dostuff.py =====
    > import dostuff
    >
    > class Mock_foo_module(object):
    > """ Mock object for foo module """
    >
    > class Bar(object):
    > def __init__(self):
    > self.name = "banana"
    >
    > def test_dostuff_get_bar_should_create_bar():
    > """ The dostuff.get_bar function should create a new Bar """
    > dostuff.foo = Mock_foo_module()
    > test_bar = dostuff.get_bar()
    > if not test_bar.name == "banana":
    > raise AssertionError("test_bar.name should be banana")
    > =====
    >
    > This is simply using an instance of a class (Mock_foo_module) to be a
    > mock 'module' object. That mock module has the required 'Bar'
    > attribute, bound to a class; so the 'dostuff' module's 'foo' attribute
    > can be replaced with our mock module object for the purpose of the
    > test.
    >
    > The mock module's Bar class behaves in an idiomatic way (setting the
    > 'name' attribute of its instance to a known value) that we use to
    > check whether the 'dostuff' module actually used our mock module.
    >
    > This can be extended to mock any module interface you like, and then
    > confirm that the module under test has actually used the module as
    > expected.
    >
    > --
    > \ "A politician is an animal which can sit on a fence and yet |
    > `\ keep both ears to the ground." -- Henry L. Mencken |
    > _o__) |
    > Ben Finney
     
    Silfheed, Mar 30, 2007
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. gamename

    Unit Testing With Function Mocking

    gamename, Mar 16, 2007, in forum: C Programming
    Replies:
    10
    Views:
    1,086
    gamename
    Mar 19, 2007
  2. mo.sparrow
    Replies:
    0
    Views:
    366
    mo.sparrow
    Aug 22, 2008
  3. Fuzzyman
    Replies:
    0
    Views:
    269
    Fuzzyman
    Aug 22, 2009
  4. Dun Peal
    Replies:
    10
    Views:
    464
    Chris Rebert
    May 3, 2011
  5. Volker Nicolai
    Replies:
    9
    Views:
    1,031
    Fabian Pilkowski
    Jul 4, 2005
Loading...

Share This Page