Skip to content

Commit d38772b

Browse files
authored
test: use request_mock for all bound model tests (#536)
Pass `request_mock` directly to the test functions to configure the mocked HTTP requests. We do not want to update/modify protected properties (the mock object) within the client class. Allows us to move the location of the client `request` function (that is mocked) in the future, without breaking the tests. A lot of the diff is from search and replaces and breaking the test arguments onto multiple lines. This is the first patch of a larger set. The next step is to use the same technique for the actual API client (not only the bound models).
1 parent 706b133 commit d38772b

File tree

15 files changed

+800
-369
lines changed

15 files changed

+800
-369
lines changed

tests/unit/actions/test_client.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66

7+
from hcloud import Client
78
from hcloud.actions import (
89
Action,
910
ActionFailedException,
@@ -16,41 +17,52 @@
1617

1718
class TestBoundAction:
1819
@pytest.fixture()
19-
def bound_running_action(self, mocked_requests):
20-
action_client = ActionsClient(client=mocked_requests)
20+
def bound_running_action(self, client: Client):
2121
# Speed up tests that run `wait_until_finished`
22-
action_client._client._poll_interval_func = lambda _: 0.0
23-
action_client._client._poll_max_retries = 3
22+
client._poll_interval_func = lambda _: 0.0
23+
client._poll_max_retries = 3
2424

2525
return BoundAction(
26-
client=action_client,
26+
client=client.actions,
2727
data=dict(id=14, status=Action.STATUS_RUNNING),
2828
)
2929

3030
def test_wait_until_finished(
31-
self, bound_running_action, mocked_requests, running_action, successfully_action
31+
self,
32+
request_mock: mock.MagicMock,
33+
bound_running_action,
34+
running_action,
35+
successfully_action,
3236
):
33-
mocked_requests.request.side_effect = [running_action, successfully_action]
37+
request_mock.side_effect = [running_action, successfully_action]
3438
bound_running_action.wait_until_finished()
35-
mocked_requests.request.assert_called_with(url="/actions/2", method="GET")
39+
request_mock.assert_called_with(url="/actions/2", method="GET")
3640

3741
assert bound_running_action.status == "success"
38-
assert mocked_requests.request.call_count == 2
42+
assert request_mock.call_count == 2
3943

4044
def test_wait_until_finished_with_error(
41-
self, bound_running_action, mocked_requests, running_action, failed_action
45+
self,
46+
request_mock: mock.MagicMock,
47+
bound_running_action,
48+
running_action,
49+
failed_action,
4250
):
43-
mocked_requests.request.side_effect = [running_action, failed_action]
51+
request_mock.side_effect = [running_action, failed_action]
4452
with pytest.raises(ActionFailedException) as exception_info:
4553
bound_running_action.wait_until_finished()
4654

4755
assert bound_running_action.status == "error"
4856
assert exception_info.value.action.id == 2
4957

5058
def test_wait_until_finished_max_retries(
51-
self, bound_running_action, mocked_requests, running_action, successfully_action
59+
self,
60+
request_mock: mock.MagicMock,
61+
bound_running_action,
62+
running_action,
63+
successfully_action,
5264
):
53-
mocked_requests.request.side_effect = [
65+
request_mock.side_effect = [
5466
running_action,
5567
running_action,
5668
successfully_action,
@@ -60,7 +72,7 @@ def test_wait_until_finished_max_retries(
6072

6173
assert bound_running_action.status == "running"
6274
assert exception_info.value.action.id == 2
63-
assert mocked_requests.request.call_count == 1
75+
assert request_mock.call_count == 1
6476

6577

6678
class TestResourceActionsClient:

tests/unit/certificates/test_client.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pytest
66

7+
from hcloud import Client
78
from hcloud.actions import BoundAction
89
from hcloud.certificates import (
910
BoundCertificate,
@@ -15,16 +16,21 @@
1516

1617
class TestBoundCertificate:
1718
@pytest.fixture()
18-
def bound_certificate(self, hetzner_client):
19-
return BoundCertificate(client=hetzner_client.certificates, data=dict(id=14))
19+
def bound_certificate(self, client: Client):
20+
return BoundCertificate(client.certificates, data=dict(id=14))
2021

2122
@pytest.mark.parametrize("params", [{"page": 1, "per_page": 10}, {}])
2223
def test_get_actions_list(
23-
self, hetzner_client, bound_certificate, response_get_actions, params
24+
self,
25+
request_mock: mock.MagicMock,
26+
client: Client,
27+
bound_certificate,
28+
response_get_actions,
29+
params,
2430
):
25-
hetzner_client.request.return_value = response_get_actions
31+
request_mock.return_value = response_get_actions
2632
result = bound_certificate.get_actions_list(**params)
27-
hetzner_client.request.assert_called_with(
33+
request_mock.assert_called_with(
2834
url="/certificates/14/actions", method="GET", params=params
2935
)
3036

@@ -33,23 +39,29 @@ def test_get_actions_list(
3339

3440
assert len(actions) == 1
3541
assert isinstance(actions[0], BoundAction)
36-
assert actions[0]._client == hetzner_client.actions
42+
assert actions[0]._client == client.actions
3743
assert actions[0].id == 13
3844
assert actions[0].command == "change_protection"
3945

40-
def test_get_actions(self, hetzner_client, bound_certificate, response_get_actions):
41-
hetzner_client.request.return_value = response_get_actions
46+
def test_get_actions(
47+
self,
48+
request_mock: mock.MagicMock,
49+
client: Client,
50+
bound_certificate,
51+
response_get_actions,
52+
):
53+
request_mock.return_value = response_get_actions
4254
actions = bound_certificate.get_actions()
4355

4456
params = {"page": 1, "per_page": 50}
4557

46-
hetzner_client.request.assert_called_with(
58+
request_mock.assert_called_with(
4759
url="/certificates/14/actions", method="GET", params=params
4860
)
4961

5062
assert len(actions) == 1
5163
assert isinstance(actions[0], BoundAction)
52-
assert actions[0]._client == hetzner_client.actions
64+
assert actions[0]._client == client.actions
5365
assert actions[0].id == 13
5466
assert actions[0].command == "change_protection"
5567

@@ -77,32 +89,41 @@ def test_bound_certificate_init(self, certificate_response):
7789
assert bound_certificate.status.error.message == "error message"
7890

7991
def test_update(
80-
self, hetzner_client, bound_certificate, response_update_certificate
92+
self,
93+
request_mock: mock.MagicMock,
94+
bound_certificate,
95+
response_update_certificate,
8196
):
82-
hetzner_client.request.return_value = response_update_certificate
97+
request_mock.return_value = response_update_certificate
8398
certificate = bound_certificate.update(name="New name")
84-
hetzner_client.request.assert_called_with(
99+
request_mock.assert_called_with(
85100
url="/certificates/14", method="PUT", json={"name": "New name"}
86101
)
87102

88103
assert certificate.id == 2323
89104
assert certificate.name == "New name"
90105

91-
def test_delete(self, hetzner_client, bound_certificate, generic_action):
92-
hetzner_client.request.return_value = generic_action
106+
def test_delete(
107+
self,
108+
request_mock: mock.MagicMock,
109+
bound_certificate,
110+
generic_action,
111+
):
112+
request_mock.return_value = generic_action
93113
delete_success = bound_certificate.delete()
94-
hetzner_client.request.assert_called_with(
95-
url="/certificates/14", method="DELETE"
96-
)
114+
request_mock.assert_called_with(url="/certificates/14", method="DELETE")
97115

98116
assert delete_success is True
99117

100118
def test_retry_issuance(
101-
self, hetzner_client, bound_certificate, response_retry_issuance_action
119+
self,
120+
request_mock: mock.MagicMock,
121+
bound_certificate,
122+
response_retry_issuance_action,
102123
):
103-
hetzner_client.request.return_value = response_retry_issuance_action
124+
request_mock.return_value = response_retry_issuance_action
104125
action = bound_certificate.retry_issuance()
105-
hetzner_client.request.assert_called_with(
126+
request_mock.assert_called_with(
106127
url="/certificates/14/actions/retry", method="POST"
107128
)
108129

tests/unit/conftest.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# pylint: disable=redefined-outer-name
2+
13
from __future__ import annotations
24

35
from unittest import mock
@@ -7,12 +9,16 @@
79
from hcloud import Client
810

911

10-
@pytest.fixture(autouse=True, scope="function")
11-
def mocked_requests():
12-
patcher = mock.patch("hcloud._client.requests")
13-
mocked_requests = patcher.start()
14-
yield mocked_requests
15-
patcher.stop()
12+
@pytest.fixture()
13+
def request_mock() -> mock.MagicMock:
14+
return mock.MagicMock()
15+
16+
17+
@pytest.fixture()
18+
def client(request_mock) -> Client:
19+
c = Client(token="TOKEN")
20+
c.request = request_mock
21+
return c
1622

1723

1824
@pytest.fixture()

0 commit comments

Comments
 (0)