我使用unittest.mock
嘲笑API。我的界面是一个在场景后面使用requests
的类。所以我做了这样的事情:
@pytest.fixture
def mocked_api_and_requests():
with mock.patch('my.thing.requests') as mock_requests:
mock_requests.post.return_value = good_credentials
api = MyApi(some='values', that='are defaults')
yield api, mock_requests
def test_my_thing_one(mocked_api_and_requests):
api, mocked_requests = mocked_api_and_requests
... # some assertion or another
def test_my_thing_two(mocked_api_and_requests):
api, mocked_requests = mocked_api_and_requests
... # some other assertions that are different
正如你可能已经看到的那样,我在这两个测试中都得到了相同的第一行,并且闻起来对我来说不够干净。
我希望能够做到这样的事情:
def test_my_thing_one(mock_requests, logged_in_api):
mock_requests.get.return_value = ...
而不是必须解压缩这些值,但我不确定是否可以使用pytest可靠地执行此操作。如果它在documentation for fixtures我完全错过了它。但它确实觉得应该有一个正确的方法去做我想做的事情。
有什么想法吗?如果我需要走这条路,我可以使用class TestGivenLoggedInApiAndMockRequests: ...
。我不太确定这里适当的模式是什么。
答案 0 :(得分:1)
使用多个灯具可以达到您想要的效果。
注意:我最低限度地修改了您的示例,以便我的答案中的代码是自包含的,但您应该能够轻松地将其调整到您的用例。
在 myapi.py
:
import requests
class MyApi:
def get_uuid(self):
return requests.get('http://httpbin.org/uuid').json()['uuid']
在 test.py
:
from unittest import mock
import pytest
from myapi import MyApi
FAKE_RESPONSE_PAYLOAD = {
'uuid': '12e77ecf-8ce7-4076-84d2-508a51b1332a',
}
@pytest.fixture
def mocked_requests():
with mock.patch('myapi.requests') as _mocked_requests:
response_mock = mock.Mock()
response_mock.json.return_value = FAKE_RESPONSE_PAYLOAD
_mocked_requests.get.return_value = response_mock
yield _mocked_requests
@pytest.fixture
def api():
return MyApi()
def test_requests_was_called(mocked_requests, api):
assert not mocked_requests.get.called
api.get_uuid()
assert mocked_requests.get.called
def test_uuid_is_returned(mocked_requests, api):
uuid = api.get_uuid()
assert uuid == FAKE_RESPONSE_PAYLOAD['uuid']
def test_actual_api_call(api): # Notice we don't mock anything here!
uuid = api.get_uuid()
assert uuid != FAKE_RESPONSE_PAYLOAD['uuid']
我没有定义一个返回元组的灯具,而是定义了两个灯具,它们可以独立地用于测试。组成这样的固定装置的一个优点是它们可以独立使用,例如,最后一个测试实际上是调用API,只是因为没有使用mock_requests
fixture。
请注意 - 要直接回答问题标题 - 您还可以将mocked_requests
作为api
灯具的先决条件,只需将其添加到参数中,如下所示:
@pytest.fixture
def api(mocked_requests):
return MyApi()
如果您运行测试套件,您会看到它有效,因为test_actual_api_call
将不再通过。
如果进行此更改,在测试中使用api
夹具也意味着在mocked_requests
的上下文中执行它,即使您未在测试中直接指定后者函数的论点。它仍然可以明确地使用它,例如,如果你想在返回的模拟上做出断言。
答案 1 :(得分:0)
如果您负担不起split your tuple fixture into two independent fixtures,现在可以使用this answer中所述的pytest-cases
插件将元组或列表治具“解包”到其他治具中。
您的代码如下:
from pytest_cases import pytest_fixture_plus
@pytest_fixture_plus(unpack_into="api,mocked_requests")
def mocked_api_and_requests():
with mock.patch('my.thing.requests') as mock_requests:
mock_requests.post.return_value = good_credentials
api = MyApi(some='values', that='are defaults')
yield api, mock_requests
def test_my_thing_one(api, mocked_requests):
... # some assertion or another
def test_my_thing_two(api, mocked_requests):
... # some other assertions that are different