print(Path.home()) defgetssh(): """Simple function to return expanded homedir ssh path.""" return Path.home() / ".ssh"# Path.home()返回的是/home/user ,拼接后/home/user/ssh
deftest_getssh(monkeypatch): # mocked return function to replace Path.home # always return '/abc' defmockreturn(): # 模拟返回 return Path("/abc")
# Application of the monkeypatch to replace Path.home # with the behavior of mockreturn defined above. monkeypatch.setattr(Path, "home", mockreturn) # 设置,当你调用Path.home时,使用mockreturn ,mockreturn返回的是/abc
# Calling getssh() will use mockreturn in place of Path.home # for this test with the monkeypatch. x = getssh() # 调用getssh(),实际调用的是mockreturn,返回的是/abc print(x) assert x == Path("/abc/.ssh")
Monkeypatching 返回对象:构建模拟类
monkeypatch.setattr也可以与类结合使用,模拟函数所返回的是对象而不是值。定义一个简单的函数,它接受 API url 并返回 json 响应。
1 2 3 4 5 6 7
# contents of app.py, a simple API retrieval example import requests
defget_json(url): """Takes a URL, and returns the JSON.""" r = requests.get(url) # 发起url请求 return r.json()
# contents of test_app.py, a simple test for our API retrieval # import requests for the purposes of monkeypatching import requests
# our app.py that includes the get_json() function # this is the previous code block example import app # 导入app.py
# custom class to be the mock return value # will override the requests.Response returned from requests.get classMockResponse: # mock json() method always returns a specific testing dictionary @staticmethod defjson(): return {"mock_key": "mock_response"} # MockResponse的.json()方法总是返回一个字典
deftest_get_json(monkeypatch): # Any arguments may be passed and mock_get() will always return our # mocked object, which only has the .json() method. defmock_get(*args, **kwargs): return MockResponse() # 返回MockResponse对象,这里返回的就是我们要mock的r对象
# apply the monkeypatch for requests.get to mock_get monkeypatch.setattr(requests, "get", mock_get) # 给requests.get方法的返回值定义为我们的mock_get所返回的MockResponse对象 # 这里就相当于什么,相当于之前的 r = requests.get("https://fakeurl") 就等于我们的r = MockResponse()所返回的MockResponse对象
# app.get_json, which contains requests.get, uses the monkeypatch result = app.get_json("https://fakeurl") # 这里就相当于就是 r.json()也相当于是我们mock的对象的mock_get()所返回的{"mock_key": "mock_response"}) assert result["mock_key"] == "mock_response"
contents of test_app.py, a simple test for our API retrieval import pytest import requests
# app.py that includes the get_json() function import app
# custom class to be the mock return value of requests.get() classMockResponse: @staticmethod defjson(): return {"mock_key": "mock_response"}
# monkeypatched requests.get moved to a fixture @pytest.fixture defmock_response(monkeypatch): """Requests.get() mocked to return {'mock_key':'mock_response'}."""
# notice our test uses the custom fixture instead of monkeypatch directly deftest_get_json(mock_response): result = app.get_json("https://fakeurl") assert result["mock_key"] == "mock_response"
@pytest.fixture(autouse=True) defno_requests(monkeypatch): """Remove requests.sessions.Session.request for all tests.""" monkeypatch.delattr("requests.sessions.Session.request")
# contents of our test file e.g. test_code.py import pytest
deftest_upper_to_lower(monkeypatch): """Set the USER env var to assert the behavior.""" monkeypatch.setenv("USER", "TestingUser") # 设置环境变量 assert get_os_user_lower() == "testinguser"
deftest_raise_exception(monkeypatch): """Remove the USER env var and assert OSError is raised.""" monkeypatch.delenv("USER", raising=False)
with pytest.raises(OSError): _ = get_os_user_lower()
# contents of app.py to generate a simple connection string DEFAULT_CONFIG = {"user": "user1", "database": "db1"}
defcreate_connection_string(config=None): """Creates a connection string from input or defaults.""" config = config or DEFAULT_CONFIG returnf"User Id={config['user']}; Location={config['database']};"
为了测试目的,我们可以将DEFAULT_CONFIG字典修补为特定值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# contents of test_app.py # app.py with the connection string function (prior code block) import app
deftest_connection(monkeypatch): # Patch the values of DEFAULT_CONFIG to specific # testing values only for this test. monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user") monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
# expected result based on the mocks expected = "User Id=test_user; Location=test_db;"
# the test uses the monkeypatched dictionary settings result = app.create_connection_string() assert result == expected
# app.py with the connection string function import app
deftest_missing_user(monkeypatch): # patch the DEFAULT_CONFIG t be missing the 'user' key monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
# Key error expected because a config is not passed, and the # default is now missing the 'user' entry. with pytest.raises(KeyError): _ = app.create_connection_string()
# app.py with the connection string function import app
# all of the mocks are moved into separated fixtures @pytest.fixture defmock_test_user(monkeypatch): """Set the DEFAULT_CONFIG user to test_user.""" monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
@pytest.fixture defmock_test_database(monkeypatch): """Set the DEFAULT_CONFIG database to test_db.""" monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")
@pytest.fixture defmock_missing_default_user(monkeypatch): """Remove the user key from DEFAULT_CONFIG""" monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)
# tests reference only the fixture mocks that are needed deftest_connection(mock_test_user, mock_test_database): expected = "User Id=test_user; Location=test_db;"
result = app.create_connection_string() assert result == expected
deftest_missing_user(mock_missing_default_user): with pytest.raises(KeyError): _ = app.create_connection_string()