Pytest----如何使用猴子补丁

简介: Pytest----如何使用猴子补丁

【原文链接】

一、猴子补丁简介

在有些场景下的测试可能需要修改全局配置或者系统变量等操作,而这些操作仅仅是为了做一些测试,不希望永久的修改,此时就需要使用猴子补丁了,猴子补丁,即monkeypatch,是一个fixture,它提供了以下方法:

monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.setattr("somemodule.obj.name", value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=None)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)

当测试结束后或者fixture执行完成后,monkeypatch中做的所有修改都将恢复

二、>通过猴子补丁临时修改函数功能

如下,可以通过猴子补丁修改Path的home属性,进而临时修改函数的功能,然后进行测试,这样测试结束后,Path的home属性并不会真的发生修改

from pathlib import Path

def getssh():
    return Path.home() / ".ssh"

def test_getssh(monkeypatch):

    def mockreturn():
        return Path("/abc")

    monkeypatch.setattr(Path, "home", mockreturn)

    x = getssh()
    assert x == Path("/abc/.ssh")

def test_home():
    print(Path.home())

执行结果如下,很明显,在test_home测试函数中,Path.home属性并没有发生修改

$ pytest -s -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('D:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items                                                                                                                                                       

test_demo.py::test_getssh PASSED
test_demo.py::test_home C:\Users\hitre
PASSED

========================================================================== 2 passed in 0.13s ===========================================================================

三、通过猴子补丁取消测试函数中request的使用

在conftest.py中编写如下代码即可

import pytest


@pytest.fixture(autouse=True)
def no_requests(monkeypatch):
    """Remove requests.sessions.Session.request for all tests."""
    monkeypatch.delattr("requests.sessions.Session.request")

四、通过猴子补丁对环境变量测测试

如下,假设get_os_user_lower函数为被测函数,用例中可以通过猴子补丁对变量进行临时设置或删除,这样可以保证测试用例的准确性,否则当环境变量被修改或者被删除后,用例的稳定性将会收到影响

test_demo.py代码如下:

import os
import pytest


def get_os_user_lower():
    username = os.getenv("USER")

    if username is None:
        raise OSError("USER environment is not set.")

    return username.lower()

def test_upper_to_lower(monkeypatch):
    monkeypatch.setenv("USER", "TestingUser")
    assert get_os_user_lower() == "testinguser"


def test_raise_exception(monkeypatch):
    monkeypatch.delenv("USER", raising=False)

    with pytest.raises(OSError):
        _ = get_os_user_lower()

执行结果如下:

$ pytest -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('D:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items                                                                                                                                                       

test_demo.py::test_upper_to_lower PASSED                                                                                                                          [ 50%]
test_demo.py::test_raise_exception PASSED                                                                                                                         [100%]

========================================================================== 2 passed in 0.13s ===========================================================================

上述代码可以通过fixture继续优化如下:

import os
import pytest


def get_os_user_lower():
    username = os.getenv("USER")

    if username is None:
        raise OSError("USER environment is not set.")

    return username.lower()

@pytest.fixture
def mock_env_user(monkeypatch):
    monkeypatch.setenv("USER", "TestingUser")

@pytest.fixture
def mock_env_missing(monkeypatch):
    monkeypatch.delenv("USER", raising=False)

def test_upper_to_lower(mock_env_user):
    assert get_os_user_lower() == "testinguser"

def test_raise_exception(mock_env_missing):
    with pytest.raises(OSError):
        _ = get_os_user_lower()

执行结果如下:

$ pytest -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('D:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items                                                                                                                                                       

test_demo.py::test_upper_to_lower PASSED                                                                                                                          [ 50%]
test_demo.py::test_raise_exception PASSED                                                                                                                         [100%]

========================================================================== 2 passed in 0.13s ===========================================================================

五、通过猴子补丁对字典数据模拟测试

test_demo.py代码如下,其中DEFAULT_CONFIG为被测字典,create_connection_string为被测函数,测试当被测字典被修改或者被删除时的情况

import pytest


DEFAULT_CONFIG = {"user": "user1", "database": "db1"}

def create_connection_string(config=None):
    config = config or DEFAULT_CONFIG
    return f"User Id={config['user']}; Location={config['database']};"


def test_connection(monkeypatch):
    monkeypatch.setitem(DEFAULT_CONFIG, "user", "test_user")
    monkeypatch.setitem(DEFAULT_CONFIG, "database", "test_db")

    expected = "User Id=test_user; Location=test_db;"

    result = create_connection_string()
    assert result == expected

def test_missing_user(monkeypatch):

    monkeypatch.delitem(DEFAULT_CONFIG, "user", raising=False)

    with pytest.raises(KeyError):
        _ = create_connection_string()

执行结果如下:

$ pytest -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('D:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items                                                                                                                                                       

test_demo.py::test_connection PASSED                                                                                                                              [ 50%]
test_demo.py::test_missing_user PASSED                                                                                                                            [100%]

========================================================================== 2 passed in 0.13s ===========================================================================

上述代码可以通过fixture进行优化,如下:

import pytest


DEFAULT_CONFIG = {"user": "user1", "database": "db1"}

def create_connection_string(config=None):
    config = config or DEFAULT_CONFIG
    return f"User Id={config['user']}; Location={config['database']};"


@pytest.fixture
def mock_test_user(monkeypatch):
    monkeypatch.setitem(DEFAULT_CONFIG, "user", "test_user")


@pytest.fixture
def mock_test_database(monkeypatch):
    monkeypatch.setitem(DEFAULT_CONFIG, "database", "test_db")


@pytest.fixture
def mock_missing_default_user(monkeypatch):
    monkeypatch.delitem(DEFAULT_CONFIG, "user", raising=False)


def test_connection(mock_test_user, mock_test_database):

    expected = "User Id=test_user; Location=test_db;"

    result = create_connection_string()
    assert result == expected


def test_missing_user(mock_missing_default_user):

    with pytest.raises(KeyError):
        _ = create_connection_string()

执行结果如下:

$ pytest -v
========================================================================= test session starts ==========================================================================
platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- D:\Python39\python.exe
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('D:\\src\\blog\\tests\\.hypothesis\\examples')
rootdir: D:\src\blog\tests, configfile: pytest.ini
plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0
collected 2 items                                                                                                                                                       

test_demo.py::test_connection PASSED                                                                                                                              [ 50%]
test_demo.py::test_missing_user PASSED                                                                                                                            [100%]

========================================================================== 2 passed in 0.12s ===========================================================================
目录
相关文章
|
18天前
|
API Windows
恶意代码分析入门--初次接触加壳的程序(chapter1_Lab01-02)
实验分析了Lab01-02.exe文件,包括上传至VirusTotal检测、使用PEiD识别壳、FreeUPX脱壳、分析导入函数及字符串。结果显示文件被UPX壳包裹,脱壳后发现其可能通过创建服务和网络连接来实现恶意行为。
18 2
恶意代码分析入门--初次接触加壳的程序(chapter1_Lab01-02)
|
7月前
|
设计模式 测试技术 Python
Python中的猴子补丁
Python中的猴子补丁
|
8月前
|
测试技术 开发者
软件测试/测试开发/全日制|Pytest参数化神器,pytest.mark.parametrize()使用
软件测试/测试开发/全日制|Pytest参数化神器,pytest.mark.parametrize()使用
|
安全 网络协议 网络安全
8.每天进步一点点---Python抓包要了解的知识
8.每天进步一点点---Python抓包要了解的知识
|
编译器 C语言 C++
了解bug以及如何解决bug------调试(使用技巧)(下)
了解bug以及如何解决bug------调试(使用技巧)
94 0
了解bug以及如何解决bug------调试(使用技巧)(下)
|
开发者 Python
Python 中的猴子补丁
Python 中的猴子补丁
196 0
|
程序员 C语言 Windows
了解bug以及如何解决bug------调试(使用技巧)(上)
了解bug以及如何解决bug------调试(使用技巧)
122 0
|
数据采集 自然语言处理 数据可视化
《大秦赋》最近有点火!于是我用Python抓取了“相关数据”,发现了这些秘密......
《大秦赋》最近有点火!于是我用Python抓取了“相关数据”,发现了这些秘密......
《大秦赋》最近有点火!于是我用Python抓取了“相关数据”,发现了这些秘密......
|
测试技术
Pytest----自动化脚本的加载原理
Pytest----自动化脚本的加载原理
493 1