I'm starting to learn Python, and along with that, I try learn how to write tests for my code. I've decided to use py.test and mock for that. I'm given a quite big and complex class, to write tests for, so for a start I decided to work on a simpler example of my own.
So, I've written a very simple class (person.py in a package called src_pkg)
class Person():
def __init__(self, name, age):
self.name = name
self.age = age
def can_do_it(self, x):
result = True if x > 5 else False
print "result: ", result
return result
What I want to do, is mock the Person class, and create an instance of the mocked class, in order to be able to call the can_do_it() method.
The reason I want to do that, is because the real class I'm working on, has a really complex constructor, and I don't want to make an instance of the class by writing something like foo = Foo(x, y, z)
So, I've written my test code (test_person.py in a package called test_pkg), which is the following:
from mock import patch
class TestPerson():
def test_can_do_it(self):
with patch('src_pck.person.Person') as Person:
person = Person.return_value
print "can he do it? :", person.can_do_it(4)
but when I run:
$ py.test -v -s test_person.py
I get the following result:
platform linux2 -- Python 2.7.5 -- py-1.4.20 -- pytest-2.5.2 -- /home/kostas/.virtualenvs/excite/bin/python
collected 1 items
test_person.py:5: TestPerson.test_can_do_it Can he do it? : <MagicMock name='Person().can_do_it()' id='37709904'>
PASSED
I would expect that the expression print "can he do it? :", person.can_do_it(4) would result to can he do it? : False. As a result, there is no point of asserting anything.
I think that when I run the test, it does not call the can_do_it() method at all! (otherwise the print statement of the method would be printed, right?)
So, what am I doing wrong?
Any help would be really appreciated.
Thank you in advance.
2 Answers 2
Patch the __init__ method using mock.patch.object:
from mock import patch
import src_pkg.person as p
class TestPerson():
def test_can_do_it(self):
with patch.object(p.Person, '__init__', lambda self: None):
person = p.Person()
print "can he do it? :", person.can_do_it(4)
3 Comments
can_do_it method. If the return value is only needed to test some other code, setting the return_value is an appropriate solution. On the other hand, if can_do_it is the part under test, this doesn't test the method.A mock object has mock methods, not the real methods. The real methods may depend on having a real, fully-constructed object of the right class as self, which a mock object can't provide. If you need to test the can_do_it method, you can't use a mock Person to do it.
If can_do_it doesn't depend on having a fully-constructed self available, you can move the implementation to a module-level function or static method and have the instance method call that:
class Person(object):
...
def can_do_it(self, x):
return _can_do_it(x)
def _can_do_it(x):
result = True if x > 5 else False
print "result: ", result
return result
Then you can just test the module-level function. If you need certain bits and pieces of a Person, but you don't need to construct the whole thing, then you can just construct (or mock) the bits and pieces and have the module-level function take them as arguments.
If can_do_it depends on having a real self or most of one, you may need to construct a real Person object and call the method.
2 Comments
self, is its log attribute. So either I'll remove this single log line contained in the method, or I'll have to create an actual Person objectlog depends on, you may be able to use a module-level function and pass it a real or mock log. (Answer expanded.)