Python:阻止网络连接以进行测试?

时间:2013-09-03 21:31:09

标签: python sandbox

我正在尝试测试一个为少数Web服务提供接口的包。它有一个测试套件,可以测试大多数函数,而不用连接到互联网。但是,有一些挥之不去的测试可能会尝试连接到互联网/下载数据,我想阻止它们这样做有两个原因:第一,确保我的测试套件在没有网络连接的情况下工作;第二,所以我不会通过多余的查询向Web服务发送垃圾邮件。

一个明显的解决方案是拔掉我的机器/关闭无线,但当我在远程机器上运行显然无效的测试时。

所以,我的问题:我可以阻止单个python进程的网络/端口访问吗? (“沙盒”,但只是阻止网络连接)

(afaict,pysandbox不这样做)

编辑:我正在使用py.test所以我需要一个适用于py.test的解决方案,以防影响任何建议的答案。

4 个答案:

答案 0 :(得分:14)

猴子修补socket应该这样做:

import socket
def guard(*args, **kwargs):
    raise Exception("I told you not to use the Internet!")
socket.socket = guard

确保在任何其他导入之前运行。

答案 1 :(得分:11)

更新:现在有一个pytest插件与此答案完全相同!您可以阅读答案,看看情况如何运作,但我强烈建议使用插件而不是复制粘贴我的答案:-)请看这里:https://github.com/miketheman/pytest-socket

我发现Thomas Orozco的回答非常有帮助。关注keflavich,这就是我整合到单元测试套件中的方式。这适用于我有数千个非常不同的单元测试用例(< 100,但需要套接字)...以及进出doctests。

我发布了here。包括以下方便。用Python 2.7.5测试,pytest == 2.7.0。 (要自己测试,请在目录中运行py.test --doctest-modules并克隆所有3个文件。)

<强> _socket_toggle.py

from __future__ import print_function
import socket
import sys

_module = sys.modules[__name__]

def disable_socket():
    """ disable socket.socket to disable the Internet. useful in testing.

    .. doctest::
        >>> enable_socket()
        [!] socket.socket is enabled.
        >>> disable_socket()
        [!] socket.socket is disabled. Welcome to the desert of the real.
        >>> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        Traceback (most recent call last):
        ...
        RuntimeError: I told you not to use the Internet!
        >>> enable_socket()
        [!] socket.socket is enabled.
        >>> enable_socket()
        [!] socket.socket is enabled.
        >>> disable_socket()
        [!] socket.socket is disabled. Welcome to the desert of the real.
        >>> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        Traceback (most recent call last):
        ...
        RuntimeError: I told you not to use the Internet!
        >>> enable_socket()
        [!] socket.socket is enabled.
    """
    setattr(_module, '_socket_disabled', True)

    def guarded(*args, **kwargs):
        if getattr(_module, '_socket_disabled', False):
            raise RuntimeError("I told you not to use the Internet!")
        else:
            # SocketType is a valid public alias of socket.socket,
            # we use it here to avoid namespace collisions
            return socket.SocketType(*args, **kwargs)

    socket.socket = guarded

    print(u'[!] socket.socket is disabled. Welcome to the desert of the real.')


def enable_socket():
    """ re-enable socket.socket to enable the Internet. useful in testing.
    """
    setattr(_module, '_socket_disabled', False)
    print(u'[!] socket.socket is enabled.')

<强> conftest.py

# Put this in the conftest.py at the top of your unit tests folder,
# so it's available to all unit tests
import pytest
import _socket_toggle


def pytest_runtest_setup():
    """ disable the interet. test-cases can explicitly re-enable """
    _socket_toggle.disable_socket()


@pytest.fixture(scope='function')
def enable_socket(request):
    """ re-enable socket.socket for duration of this test function """
    _socket_toggle.enable_socket()
    request.addfinalizer(_socket_toggle.disable_socket)

<强> test_example.py

# Example usage of the py.test fixture in tests
import socket
import pytest

try:
    from urllib2 import urlopen
except ImportError:
    import urllib3
    urlopen = urllib.request.urlopen


def test_socket_disabled_by_default():
    # default behavior: socket.socket is unusable
    with pytest.raises(RuntimeError):
        urlopen(u'https://www.python.org/')


def test_explicitly_enable_socket(enable_socket):
    # socket is enabled by pytest fixture from conftest. disabled in finalizer
    assert socket.socket(socket.AF_INET, socket.SOCK_STREAM)

答案 2 :(得分:0)

requests库上添加插科打simple的简单方法:

from unittest import mock

requests_gag = mock.patch(
    'requests.Session.request',
    mock.Mock(side_effect=RuntimeError(
        'Please use the `responses` library to mock HTTP in your tests.'
    ))
)

with requests_gag:
    ...  # no Internet here


答案 3 :(得分:0)

基于Thomas Orozco和driftcatcher的非常有帮助的答案,这里是一个变体,可与Python的unittest和Django(稍作改动后)一起使用。

您需要做的就是从增强的import win32com.client ExcelApp = win32com.client.Dispatch("Excel.Application") ExcelApp.Visible = False workbook =ExcelApp.Workbooks.Open(r'C:\Users\eshsubh\Documents\EKN\DL MS1.xlsx') excelrange = worksheet.Range("A2:R24") excelrange.Copy() 类继承您的测试用例类,并且将检测到对网络的任何访问并引发NoSocketTestCase异常。

这种方法也适用于Django。您只需更改SocketAccessError类即可从NoSocketTestCase继承,而不是django.test.TestCase

虽然没有严格回答OP的问题,但我认为这对于希望在单元测试中阻止网络访问的任何人都是有帮助的。

no_sockets.py

unittest.TestCase

test_no_sockets.py

import socket
from unittest import TestCase


class SocketAccessError(Exception):
    pass


class NoSocketsTestCase(TestCase):
    """Enhancement of TestCase class that prevents any use of sockets

    Will throw the exception SocketAccessError when any code tries to
    access network sockets
    """

    @classmethod
    def setUpClass(cls):
        cls.socket_original = socket.socket
        socket.socket = cls.guard
        return super().setUpClass()

    @classmethod
    def tearDownClass(cls):
        socket.socket = cls.socket_original
        return super().tearDownClass()

    @staticmethod
    def guard(*args, **kwargs):
        raise SocketAccessError('Attempted to access network')