您已经走上了正确的道路,但是有几个问题需要解决才能使这个测试正常工作。
首先,您的问题之一是
patch
传递给测试方法的模拟对象被称为
mockSocket
,但您的测试代码引用了一个名为
mock_socket
的对象。
其次,
patch
的第一个参数应该是要修补的模块路径的
字符串表示形式。如果您的文件结构如下所示:
|-- root_directory
| |
| |-- app_directory
| | |-- socketHandler.py
| | `-- somethingElse.py
| |
| `-- test_directory
| |-- testSocketHandler.py
| `-- testSomethingElse.py
如果你从根目录运行测试,你需要像这样调用patch:@mock.patch("app_directory.socketHandler.socket")
构造函数被调用 - 最重要的是要意识到mockSocket
是一个表示socket类的Mock
对象。因此,要测试构造函数是否被调用,你需要检查mockSocket.assert_called_with(...)
。如果你的生产代码调用了socket(...)
,这个测试就会通过。
你可能还想断言mySocketHandler.socket
是与mockSocket.return_value
相同的对象,以测试mySocketHandler不仅调用了构造函数,而且将其分配给了正确的属性。
和3. connect
和sendall
被正确调用 - 你不应该在测试中调用你的mock,因为这可能导致虚假的通过断言。换句话说,你希望你的生产代码是唯一调用mocks的东西。这意味着你不应该使用new_sock = mock_socket()
这一行,因为这样你之前关于构造函数的断言将无论你的生产代码做什么都会通过,我认为它导致了其他断言失败。
mockSocket
已经是Mock
的一个实例,因此它的返回值将自动是另一个不同的Mock
实例。因此,你不需要上面测试代码中的前两行,而且你只需要在connect
上进行一个断言。对于sendall
也是同样的道理。
这是一堆需要注意的事情,如果我写了这个测试,它会是这个样子:
from unittest import mock, TestCase
import pytest
import socketHandler
class TestSocketHandler(TestCase):
@mock.patch("app_directory.socketHandler.socket")
def test_socket_handler(mockSocketClass):
mySocketHandler = SocketHandler(...)
mockSocketClass.assert_called_with(...)
self.assertIs(mockSocketClass.return_value, mySocketHandler.socket)
mockSocketClass.return_value.connect.assert_called_with(...)
mockSocketClass.return_value.sendall.assert_called_with(expectedMessage)
奖励环节!MagicMock
类似于Mock
,但它们为某些魔法方法实现了一些默认值。除非我绝对需要它们,否则我不使用它们。以下是一个示例:
from mock import Mock, MagicMock
mock = Mock()
magic_mock = MagicMock()
int(mock)
>>>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'Mock'
len(mock)
>>>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'Mock' has no len()
int(magic_mock)
>>> 1
len(magic_mock)
>>> 0
Mock
的spec。我避免使用它们,因为我很少需要为自己定义任何魔术方法,并且我不希望我的测试在我忘记在MagicMock
上覆盖魔术方法时通过。 - lortimer