Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock a class in Python in order to call one of its methods

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.

like image 865
kostasg Avatar asked Feb 14 '14 05:02

kostasg


2 Answers

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.

like image 156
user2357112 supports Monica Avatar answered Sep 19 '22 12:09

user2357112 supports Monica


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)
like image 25
falsetru Avatar answered Sep 18 '22 12:09

falsetru