Python automatic testing: mocking an imported module?

S

Silfheed

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?
 
C

Chris Lasher

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
 
B

Ben Finney

Silfheed said:
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.
 
P

Peter Otten

Silfheed said:
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
 
M

Mark T

Silfheed said:
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
 
R

rocky.bernstein

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.)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top