如何在 python 中模拟套接字模块?

How to mock socket module in python?

我正在使用 python3 进行多播项目,并且有一个小程序可以在本地网络上搜索 upnp 设备。我想使用 unittestmocksocket.recvfrom(bufsize[, flags]).

的昂贵调用

这是我的程序./upnp/mucassdp.py:

#!/usr/bin/env python3

import socket

class ssdpObj():
# Handle Simple Service Discovery Protocol,
# for discovering UPnP devices on the local network

    def msearch(self):
        msg = \
            'M-SEARCH * HTTP/1.1\r\n' \
            'HOST:239.255.255.250:1900\r\n' \
            'ST:upnp:rootdevice\r\n' \
            'MX:2\r\n' \
            'MAN:"ssdp:discover"\r\n' \
            '\r\n'

        # Set up UDP socket with timeout and send a M-SEARCH structure
        # to the upnp multicast address and port
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        s.settimeout(2)
        s.sendto(msg.encode(), ('239.255.255.250', 1900) )

        # print received data within the timeout
        try:
            while True:
                # expensive call to mock for testing
                data, addr = s.recvfrom(65507)

                print (addr, data.decode(), end='')
        except socket.timeout:
            pass

这是我的测试用例 ./tests/mucassdpTest.py:

#!/usr/bin/env python3

import unittest
from unittest.mock import patch

from upnp.mucassdp import ssdpObj


class mucassdpTestCase(unittest.TestCase):

    @patch('upnp.mucassdp.socket')
    def test_msearch(self, mock_socket):
        # instantiate our service
        oSsdp = ssdpObj()

        mock_socket.recvfrom.return_value = [0, '1']
        #mock_socket.recvfrom.side_effect = [0, '1']

        oSsdp.msearch()
        mock_socket.settimeout.assert_called_with(2)

当我尝试 运行 测试用例时:

~$ python3 -m unittest tests.mucassdpTest

我收到错误消息(仅相关部分):

ERROR: test_msearch (tests.mucassdpTest.mucassdpTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/ingo/devel/muca-tools/upnp/mucassdp.py", line 27, in msearch
    data, addr = s.recvfrom(65507)
ValueError: not enough values to unpack (expected 2, got 0)

我环顾四周,发现了一些类似的问题和答案,例如使用预期的 data, addr 或设置 side_effect 通过其构造函数初始化 Mock (我试过这个)但我无法通过我的测试成功地将它设置为 运行。

我必须如何设置修补的 socket 模块才能在测试条件下从 s.recvfrom(...) 获得预期的配对 data, addr

这里的问题是您在 upnp.mucassdp 中模拟 socket 模块导入,然后尝试使用 recvfrom() 调用对其进行配置。但是,recvfrom() 方法存在于 socket.socket class,而不是 socket 模块。

@patch('upnp.mucassdp.socket')
def test_msearch(self, mock_socket):
    # mock_socket is the module, not the socket class

解决方法是打补丁socket.socketclass,然后在测试中,获取实例再配置。

class mucassdpTestCase(unittest.TestCase):

    @patch('upnp.mucassdp.socket.socket')  # Patch the class
    def test_msearch(self, mock_socket):
        mock_socket = mock_socket.return_value  # We want the instance

        # instantiate our service
        oSsdp = ssdpObj()

        mock_socket.recvfrom.return_value = [0, '1']
        #mock_socket.recvfrom.side_effect = [0, '1']

        oSsdp.msearch()
        mock_socket.settimeout.assert_called_with(2)