Skip to content

Commit 6505604

Browse files
authored
Refactor some test fixtures / setup (#205)
* Decouple test objects from mocked service side effects In some of our fixtures, we'd return an example test object, but also specify the returns of mocked side effects when that fixture was used. This made it slightly more challenging to modify test fixtures. It also wasn't obvious that the service mocking was happening when reading tests. Now, we set the return values of mocked services in tests. This makes tests a bit more verbose, but also makes them more explicit and obvious. * Add a tests/fixtures/factories.py module to facilitate easy test object creation These factory functions aren't perfect, and eventually it would be good to use something like 1pydantic-factories1, but for now this is a good-enough approach to easily make different examaple objects in tests. * Move some action tests from test_configuration.py to test_models.py Some of the Action tests in test_configuration.py were actually testing the Action validators, so it seemed more appropriate to have them live in test_models.py.
1 parent a9e3d2c commit 6505604

File tree

9 files changed

+337
-309
lines changed

9 files changed

+337
-309
lines changed

tests/conftest.py

Lines changed: 54 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
from jbi.app import app
1111
from jbi.environment import Settings
1212
from jbi.models import Action, Actions, BugzillaWebhookComment, BugzillaWebhookRequest
13+
from tests.fixtures.factories import (
14+
action_factory,
15+
bug_factory,
16+
webhook_event_factory,
17+
webhook_factory,
18+
webhook_user_factory,
19+
)
1320

1421

1522
@pytest.fixture
@@ -27,207 +34,95 @@ def settings():
2734
@pytest.fixture(autouse=True)
2835
def mocked_bugzilla():
2936
with mock.patch("jbi.services.rh_bugzilla.Bugzilla") as mocked_bz:
30-
yield mocked_bz
37+
yield mocked_bz()
3138

3239

3340
@pytest.fixture(autouse=True)
3441
def mocked_jira():
3542
with mock.patch("jbi.services.Jira") as mocked_jira:
36-
yield mocked_jira
43+
yield mocked_jira()
3744

3845

3946
@pytest.fixture
40-
def webhook_create_example(mocked_bugzilla) -> BugzillaWebhookRequest:
41-
webhook_payload = BugzillaWebhookRequest.parse_obj(
42-
{
43-
"bug": {
44-
"assigned_to": "[email protected]",
45-
"comment": None,
46-
"component": "General",
47-
"creator": "[email protected]",
48-
"flags": [],
49-
"id": 654321,
50-
"is_private": False,
51-
"keywords": [],
52-
"priority": "",
53-
"product": "JBI",
54-
"resolution": "",
55-
"see_also": [],
56-
"severity": "--",
57-
"status": "NEW",
58-
"summary": "JBI Test",
59-
"type": "defect",
60-
"whiteboard": "devtest",
61-
},
62-
"event": {
63-
"action": "create",
64-
"changes": None,
65-
"routing_key": "bug.create",
66-
"target": "bug",
67-
"time": "2022-03-23T20:10:17.495000+00:00",
68-
"user": {
69-
"id": 123456,
70-
"login": "[email protected]",
71-
"real_name": "Nobody [ :nobody ]",
72-
},
73-
},
74-
"webhook_id": 34,
75-
"webhook_name": "local-test",
76-
}
77-
)
78-
79-
mocked_bugzilla().getbug.return_value = webhook_payload.bug
80-
mocked_bugzilla().get_comments.return_value = {
81-
"bugs": {"654321": {"comments": [{"text": "Initial comment"}]}}
82-
}
47+
def webhook_create_example() -> BugzillaWebhookRequest:
48+
webhook_payload = webhook_factory()
8349

8450
return webhook_payload
8551

8652

8753
@pytest.fixture
88-
def webhook_comment_example(webhook_create_example) -> BugzillaWebhookRequest:
89-
webhook_comment_example: BugzillaWebhookRequest = webhook_create_example
90-
webhook_comment_example.event.target = "comment"
91-
webhook_comment_example.event.user.login = "[email protected]" # type: ignore
92-
assert webhook_comment_example.bug
93-
webhook_comment_example.bug.comment = BugzillaWebhookComment.parse_obj(
94-
{"number": 2, "body": "hello"}
54+
def webhook_comment_example() -> BugzillaWebhookRequest:
55+
user = webhook_user_factory(login="[email protected]")
56+
comment = BugzillaWebhookComment.parse_obj({"number": 2, "body": "hello"})
57+
bug = bug_factory(
58+
see_also=["https://mozilla.atlassian.net/browse/JBI-234"],
59+
comment=comment,
9560
)
96-
webhook_comment_example.bug.see_also = [
97-
"https://mozilla.atlassian.net/browse/JBI-234"
98-
]
99-
return webhook_comment_example
61+
event = webhook_event_factory(target="comment", user=user)
62+
webhook_payload = webhook_factory(bug=bug, event=event)
63+
64+
return webhook_payload
10065

10166

10267
@pytest.fixture
103-
def webhook_private_comment_example(
104-
webhook_create_example, mocked_bugzilla
105-
) -> BugzillaWebhookRequest:
106-
webhook_comment_example: BugzillaWebhookRequest = webhook_create_example
107-
webhook_comment_example.event.target = "comment"
108-
webhook_comment_example.event.user.login = "[email protected]" # type: ignore
109-
assert webhook_comment_example.bug
110-
webhook_comment_example.bug.comment = BugzillaWebhookComment.parse_obj(
111-
{"id": 344, "number": 2, "is_private": True}
68+
def webhook_private_comment_example() -> BugzillaWebhookRequest:
69+
user = webhook_user_factory(login="[email protected]")
70+
event = webhook_event_factory(target="comment", user=user)
71+
bug = bug_factory(
72+
comment={"id": 344, "number": 2, "is_private": True},
73+
see_also=["https://mozilla.atlassian.net/browse/JBI-234"],
11274
)
113-
webhook_comment_example.bug.see_also = [
114-
"https://mozilla.atlassian.net/browse/JBI-234"
115-
]
116-
117-
# Call to Bugzilla returns the actual bug comments.
118-
mocked_bugzilla().get_comments.return_value = {
119-
"bugs": {
120-
str(webhook_comment_example.bug.id): {
121-
"comments": [
122-
{
123-
"id": 343,
124-
"text": "not this one",
125-
"bug_id": webhook_comment_example.bug.id,
126-
"count": 1,
127-
"is_private": True,
128-
"creator": "[email protected]",
129-
},
130-
{
131-
"id": 344,
132-
"text": "hello",
133-
"bug_id": webhook_comment_example.bug.id,
134-
"count": 2,
135-
"is_private": True,
136-
"creator": "[email protected]",
137-
},
138-
{
139-
"id": 345,
140-
"text": "or this one",
141-
"bug_id": webhook_comment_example.bug.id,
142-
"count": 3,
143-
"is_private": True,
144-
"creator": "[email protected]",
145-
},
146-
]
147-
}
148-
},
149-
"comments": {},
150-
}
151-
152-
return webhook_comment_example
75+
webhook_payload = webhook_factory(bug=bug, event=event)
76+
return webhook_payload
15377

15478

15579
@pytest.fixture
156-
def webhook_create_private_example(
157-
webhook_create_example, mocked_bugzilla
158-
) -> BugzillaWebhookRequest:
159-
private_bug = webhook_create_example.bug
160-
private_bug.is_private = True
161-
# Call to Bugzilla returns the actual bug object.
162-
mocked_bugzilla().getbug.return_value = private_bug
163-
164-
# But webhook payload only contains private attribute.
165-
webhook_create_private_example = BugzillaWebhookRequest.parse_obj(
166-
{
167-
**webhook_create_example.dict(),
168-
"bug": {"id": private_bug.id, "is_private": True},
169-
}
80+
def webhook_create_private_example() -> BugzillaWebhookRequest:
81+
return webhook_factory(
82+
event=webhook_event_factory(),
83+
bug={"id": 654321, "is_private": True},
17084
)
171-
return webhook_create_private_example
17285

17386

17487
@pytest.fixture
175-
def webhook_modify_example(webhook_create_example) -> BugzillaWebhookRequest:
176-
webhook_modify_example: BugzillaWebhookRequest = webhook_create_example
177-
assert webhook_modify_example.bug
178-
webhook_modify_example.bug.see_also = [
179-
"https://mozilla.atlassian.net/browse/JBI-234"
180-
]
181-
182-
webhook_modify_example.event.action = "modify"
183-
webhook_modify_example.event.routing_key = "bug.modify:status"
184-
return webhook_modify_example
88+
def webhook_modify_example() -> BugzillaWebhookRequest:
89+
bug = bug_factory(see_also=["https://mozilla.atlassian.net/browse/JBI-234"])
90+
event = webhook_event_factory(action="modify", routing_key="bug.modify:status")
91+
webhook_payload = webhook_factory(bug=bug, event=event)
92+
return webhook_payload
18593

18694

18795
@pytest.fixture
188-
def webhook_change_status_assignee(webhook_modify_example):
189-
payload = webhook_modify_example.dict()
190-
payload["event"]["routing_key"] = "bug.modify"
191-
payload["event"]["changes"] = [
192-
{"field": "status", "removed": "OPEN", "added": "FIXED"},
96+
def webhook_change_status_assignee():
97+
changes = [
98+
{
99+
"field": "status",
100+
"removed": "OPEN",
101+
"added": "FIXED",
102+
},
193103
{
194104
"field": "assignee",
195105
"removed": "[email protected]",
196106
"added": "[email protected]",
197107
},
198108
]
199-
return BugzillaWebhookRequest.parse_obj(payload)
109+
event = webhook_event_factory(routing_key="bug.modify", changes=changes)
110+
webhook_payload = webhook_factory(event=event)
111+
return webhook_payload
200112

201113

202114
@pytest.fixture
203-
def webhook_modify_private_example(
204-
webhook_modify_example, mocked_bugzilla
205-
) -> BugzillaWebhookRequest:
206-
private_bug = webhook_modify_example.bug
207-
private_bug.is_private = True
208-
# Call to Bugzilla returns the actual bug object.
209-
mocked_bugzilla().getbug.return_value = private_bug
210-
211-
# But webhook payload only contains private attribute.
212-
webhook_modify_private_example = BugzillaWebhookRequest.parse_obj(
213-
{
214-
**webhook_modify_example.dict(),
215-
"bug": {"id": private_bug.id, "is_private": True},
216-
}
115+
def webhook_modify_private_example() -> BugzillaWebhookRequest:
116+
event = webhook_event_factory(action="modify", routing_key="bug.modify:status")
117+
webhook_payload = webhook_factory(
118+
bug={"id": 654321, "is_private": True}, event=event
217119
)
218-
return webhook_modify_private_example
120+
return webhook_payload
219121

220122

221123
@pytest.fixture
222124
def action_example() -> Action:
223-
return Action.parse_obj(
224-
{
225-
"whiteboard_tag": "devtest",
226-
"contact": "[email protected]",
227-
"description": "test config",
228-
"module": "tests.fixtures.noop_action",
229-
}
230-
)
125+
return action_factory()
231126

232127

233128
@pytest.fixture

tests/fixtures/factories.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from jbi.models import (
2+
Action,
3+
BugzillaBug,
4+
BugzillaWebhookEvent,
5+
BugzillaWebhookRequest,
6+
BugzillaWebhookUser,
7+
)
8+
9+
10+
def action_factory(**overrides):
11+
action = {
12+
"whiteboard_tag": "devtest",
13+
"contact": "tbd",
14+
"description": "test config",
15+
"module": "tests.fixtures.noop_action",
16+
**overrides,
17+
}
18+
return Action.parse_obj(action)
19+
20+
21+
def bug_factory(**overrides):
22+
bug = {
23+
"assigned_to": "[email protected]",
24+
"comment": None,
25+
"component": "General",
26+
"creator": "[email protected]",
27+
"flags": [],
28+
"id": 654321,
29+
"is_private": False,
30+
"keywords": [],
31+
"priority": "",
32+
"product": "JBI",
33+
"resolution": "",
34+
"see_also": [],
35+
"severity": "--",
36+
"status": "NEW",
37+
"summary": "JBI Test",
38+
"type": "defect",
39+
"whiteboard": "devtest",
40+
**overrides,
41+
}
42+
return BugzillaBug.parse_obj(bug)
43+
44+
45+
def webhook_user_factory(**overrides):
46+
user = {
47+
"id": 123456,
48+
"login": "[email protected]",
49+
"real_name": "Nobody [ :nobody ]",
50+
**overrides,
51+
}
52+
return BugzillaWebhookUser.parse_obj(user)
53+
54+
55+
def webhook_event_factory(**overrides):
56+
event = {
57+
"action": "create",
58+
"changes": None,
59+
"routing_key": "bug.create",
60+
"target": "bug",
61+
"time": "2022-03-23T20:10:17.495000+00:00",
62+
"user": webhook_user_factory(),
63+
**overrides,
64+
}
65+
return BugzillaWebhookEvent.parse_obj(event)
66+
67+
68+
def webhook_factory(**overrides):
69+
webhook_event = {
70+
"bug": bug_factory(),
71+
"event": webhook_event_factory(),
72+
"webhook_id": 34,
73+
"webhook_name": "local-test",
74+
**overrides,
75+
}
76+
return BugzillaWebhookRequest.parse_obj(webhook_event)
77+
78+
79+
def comment_factory(**overrides):
80+
return {
81+
"id": 343,
82+
"text": "comment text",
83+
"bug_id": 654321,
84+
"count": 1,
85+
"is_private": True,
86+
"creator": "[email protected]",
87+
**overrides,
88+
}

0 commit comments

Comments
 (0)