Почему Python mock patch не работает?

у меня два файла

spike.py

class T1(object):
    def foo(self, afd):
        return "foo"

    def get_foo(self):
        return self.foo(1)


def bar():
    return "bar"

test_spike.py:

from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1, bar


class TestStuff(TestCase):
    @patch('spike.T1.foo', MagicMock(return_value='patched'))
    def test_foo(self):
        foo = T1().get_foo()
        self.assertEqual('patched', foo)

    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        bar = bar()
        self.assertEqual('patched', bar)


if __name__ == "__main__":
    unittest.main()

когда я запускаю python test_spike.py, первый тест пройдет, но второй не. и я переключаюсь на использование nosetests test_spike.py, то оба два не удалось.

Я не понимаю, как это произошло? Эти дела должны пройти все.

2 ответов


на test_foo вы неправильно используете патч. Вы должны использовать его следующим образом:

class TestFoo(TestCase):
@patch.object(T1, 'foo', MagicMock(return_value='patched'))
def test_foo(self):
    foo = T1().get_foo()
    self.assertEqual('patched', foo)

это дает мне:

nosetests test_spike.py 
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

теперь второй пример не работает, потому что вы импортируете функцию бара (получаете ссылку на нее), а затем пытаетесь издеваться над ней. Когда вы издеваетесь над чем-то, вы не можете изменить то, что ваши переменные содержат (ссылка на исходную функцию). Чтобы исправить это, вы должны использовать @falsetru предложенный метод, как:

from unittest import TestCase
import unittest
from mock import patch
import spike


class TestFoo(TestCase):
    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        value = spike.bar()
        self.assertEqual('patched', value)


if __name__ == "__main__":
    unittest.main()

это дает я:

python test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

но когда я пытаюсь запустить его с носа я:

 nosetests test_spike.py
F
======================================================================
FAIL: test_bar (src.test_spike.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/zilva/envs/test/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
    return func(*args, **keywargs)
  File "/home/zilva/git/test/src/test_spike.py", line 11, in test_bar
    self.assertEqual('patched', value)
AssertionError: 'patched' != 'bar'

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)

это происходит потому, что я латаю не в нужном месте. Моя структура каталогов:

test/
└── src/
    ├── spike.py
    ├── test_spike.py
    └── __init__.py

и я запускаю тесты из каталога src, поэтому я должен исправлять путь из корневого каталога проекта, например:

@patch('src.spike.bar')

и это дало бы мне:

nosetests test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

или если я нахожусь в тестовом каталоге:

nosetests src/test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

открыть bar С помощью spike.bar. Импортные bar не зависит от mock.patch.

from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1
import spike # <----


class TestShit(TestCase):
    @patch('spike.T1.foo', MagicMock(return_value='patched'))
    def test_foo(self):
        foo = T1().get_foo()
        self.assertEqual('patched', foo)

    @patch('spike.bar')
    def test_bar(self, mock_obj):
        mock_obj.return_value = 'patched'
        bar = spike.bar() # <-----
        self.assertEqual('patched', bar)


if __name__ == "__main__":
    unittest.main()