12

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

6

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) 
Sign up to request clarification or add additional context in comments.

3 Comments

It looks like the purpose of the test is to test the 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.
@falsetru: thank you for the answer. The thing is that, as user2357112 said, I don't want to mock the method call, and set it return a standard result. I want to test the actual method, so the above suggestion does not help me. And indeed there is a typo, but it is actually the other way around :) I'll try to fix that. Thanks anyway
@kostasg, I misunderstood your question. I updated the answer.
2

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

thank you for the answer. The thing is, that actually the only part that the method is using from the 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 object
@kostasg: Depending on what the log depends on, you may be able to use a module-level function and pass it a real or mock log. (Answer expanded.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.