为什么Python模拟补丁不起作用?

问题描述 投票:0回答:3

我有两个文件

尖峰.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
,然后两个都失败了。

我不明白这是怎么发生的?这些案件应该都会通过。

python unit-testing mocking nose
3个回答
62
投票

使用

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

39
投票

对于 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

现在第二个示例不起作用,因为您导入了 bar 函数(获取对它的引用),然后尝试模拟它。当您模拟某些内容时,您无法更改变量所保存的内容(参考原始函数)。要解决此问题,您应该使用 @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

35
投票

为了详细说明非常有用的最佳答案,让我解释一下 unittest.mock 的官方文档。

a.py
    class SomeClass:
        ...

b.py
    import a
    from a import SomeClass
    def some_function():
        a.SomeClass()
        SomeClass()

如果你写

mock.patch('a.SomeClass')
,这会影响
some_function
的第一行。如果你写
mock.patch('b.SomeClass')
,这会影响第二行。

© www.soinside.com 2019 - 2024. All rights reserved.