Skip to content

Commit aedc5d9

Browse files
authored
Convert ResourceRelatedField test to pytest styled tests (#887)
1 parent 5488b18 commit aedc5d9

File tree

6 files changed

+229
-131
lines changed

6 files changed

+229
-131
lines changed

Diff for: example/settings/dev.py

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"example",
2828
"debug_toolbar",
2929
"django_filters",
30+
"tests",
3031
]
3132

3233
TEMPLATES = [

Diff for: example/tests/test_relations.py

-113
Original file line numberDiff line numberDiff line change
@@ -6,130 +6,17 @@
66
from rest_framework.fields import SkipField
77
from rest_framework.reverse import reverse
88

9-
from rest_framework_json_api.exceptions import Conflict
109
from rest_framework_json_api.relations import (
1110
HyperlinkedRelatedField,
1211
ResourceRelatedField,
1312
SerializerMethodHyperlinkedRelatedField,
1413
)
15-
from rest_framework_json_api.utils import format_resource_type
1614

1715
from . import TestBase
1816
from example.models import Author, Blog, Comment, Entry
19-
from example.serializers import CommentSerializer
2017
from example.views import EntryViewSet
2118

2219

23-
class TestResourceRelatedField(TestBase):
24-
def setUp(self):
25-
super(TestResourceRelatedField, self).setUp()
26-
self.blog = Blog.objects.create(name="Some Blog", tagline="It's a blog")
27-
self.entry = Entry.objects.create(
28-
blog=self.blog,
29-
headline="headline",
30-
body_text="body_text",
31-
pub_date=timezone.now(),
32-
mod_date=timezone.now(),
33-
n_comments=0,
34-
n_pingbacks=0,
35-
rating=3,
36-
)
37-
for i in range(1, 6):
38-
name = "some_author{}".format(i)
39-
self.entry.authors.add(
40-
Author.objects.create(name=name, email="{}@example.org".format(name))
41-
)
42-
43-
self.comment = Comment.objects.create(
44-
entry=self.entry,
45-
body="testing one two three",
46-
author=Author.objects.first(),
47-
)
48-
49-
def test_data_in_correct_format_when_instantiated_with_blog_object(self):
50-
serializer = BlogFKSerializer(instance={"blog": self.blog})
51-
52-
expected_data = {"type": format_resource_type("Blog"), "id": str(self.blog.id)}
53-
54-
actual_data = serializer.data["blog"]
55-
56-
self.assertEqual(actual_data, expected_data)
57-
58-
def test_data_in_correct_format_when_instantiated_with_entry_object(self):
59-
serializer = EntryFKSerializer(instance={"entry": self.entry})
60-
61-
expected_data = {
62-
"type": format_resource_type("Entry"),
63-
"id": str(self.entry.id),
64-
}
65-
66-
actual_data = serializer.data["entry"]
67-
68-
self.assertEqual(actual_data, expected_data)
69-
70-
def test_deserialize_primitive_data_blog(self):
71-
serializer = BlogFKSerializer(
72-
data={
73-
"blog": {"type": format_resource_type("Blog"), "id": str(self.blog.id)}
74-
}
75-
)
76-
77-
self.assertTrue(serializer.is_valid())
78-
self.assertEqual(serializer.validated_data["blog"], self.blog)
79-
80-
def test_validation_fails_for_wrong_type(self):
81-
with self.assertRaises(Conflict) as cm:
82-
serializer = BlogFKSerializer(
83-
data={"blog": {"type": "Entries", "id": str(self.blog.id)}}
84-
)
85-
serializer.is_valid()
86-
the_exception = cm.exception
87-
self.assertEqual(the_exception.status_code, 409)
88-
89-
def test_serialize_many_to_many_relation(self):
90-
serializer = EntryModelSerializer(instance=self.entry)
91-
92-
type_string = format_resource_type("Author")
93-
author_pks = Author.objects.values_list("pk", flat=True)
94-
expected_data = [{"type": type_string, "id": str(pk)} for pk in author_pks]
95-
96-
self.assertEqual(serializer.data["authors"], expected_data)
97-
98-
def test_deserialize_many_to_many_relation(self):
99-
type_string = format_resource_type("Author")
100-
author_pks = Author.objects.values_list("pk", flat=True)
101-
authors = [{"type": type_string, "id": pk} for pk in author_pks]
102-
103-
serializer = EntryModelSerializer(data={"authors": authors, "comments": []})
104-
105-
self.assertTrue(serializer.is_valid())
106-
self.assertEqual(
107-
len(serializer.validated_data["authors"]), Author.objects.count()
108-
)
109-
for author in serializer.validated_data["authors"]:
110-
self.assertIsInstance(author, Author)
111-
112-
def test_read_only(self):
113-
serializer = EntryModelSerializer(
114-
data={"authors": [], "comments": [{"type": "Comments", "id": 2}]}
115-
)
116-
serializer.is_valid(raise_exception=True)
117-
self.assertNotIn("comments", serializer.validated_data)
118-
119-
def test_invalid_resource_id_object(self):
120-
comment = {
121-
"body": "testing 123",
122-
"entry": {"type": "entry"},
123-
"author": {"id": "5"},
124-
}
125-
serializer = CommentSerializer(data=comment)
126-
assert not serializer.is_valid()
127-
assert serializer.errors == {
128-
"author": ["Invalid resource identifier object: missing 'type' attribute"],
129-
"entry": ["Invalid resource identifier object: missing 'id' attribute"],
130-
}
131-
132-
13320
class TestHyperlinkedFieldBase(TestBase):
13421
def setUp(self):
13522
super(TestHyperlinkedFieldBase, self).setUp()

Diff for: tests/conftest.py

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22

3+
from tests.models import ForeignKeyTarget, ManyToManySource, ManyToManyTarget
4+
35

46
@pytest.fixture(autouse=True)
57
def use_rest_framework_json_api_defaults(settings):
@@ -19,3 +21,23 @@ def use_rest_framework_json_api_defaults(settings):
1921
settings.JSON_API_FORMAT_FIELD_NAMES = False
2022
settings.JSON_API_FORMAT_TYPES = False
2123
settings.JSON_API_PLURALIZE_TYPES = False
24+
25+
26+
@pytest.fixture
27+
def foreign_key_target(db):
28+
return ForeignKeyTarget.objects.create(name="Target")
29+
30+
31+
@pytest.fixture
32+
def many_to_many_source(db, many_to_many_targets):
33+
source = ManyToManySource.objects.create(name="Source")
34+
source.targets.add(*many_to_many_targets)
35+
return source
36+
37+
38+
@pytest.fixture
39+
def many_to_many_targets(db):
40+
return [
41+
ManyToManyTarget.objects.create(name="Target1"),
42+
ManyToManyTarget.objects.create(name="Target2"),
43+
]

Diff for: tests/serializers.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from rest_framework_json_api.relations import ResourceRelatedField
2+
from rest_framework_json_api.serializers import ModelSerializer
3+
from tests.models import (
4+
BasicModel,
5+
ForeignKeySource,
6+
ForeignKeyTarget,
7+
ManyToManySource,
8+
ManyToManyTarget,
9+
)
10+
11+
12+
class BasicModelSerializer(ModelSerializer):
13+
class Meta:
14+
fields = ("text",)
15+
model = BasicModel
16+
17+
18+
class ForeignKeySourceSerializer(ModelSerializer):
19+
target = ResourceRelatedField(queryset=ForeignKeyTarget.objects)
20+
21+
class Meta:
22+
model = ForeignKeySource
23+
fields = ("target",)
24+
25+
26+
class ManyToManySourceSerializer(ModelSerializer):
27+
targets = ResourceRelatedField(many=True, queryset=ManyToManyTarget.objects)
28+
29+
class Meta:
30+
model = ManyToManySource
31+
fields = ("targets",)
32+
33+
34+
class ManyToManyTargetSerializer(ModelSerializer):
35+
class Meta:
36+
model = ManyToManyTarget
37+
38+
39+
class ManyToManySourceReadOnlySerializer(ModelSerializer):
40+
targets = ResourceRelatedField(many=True, read_only=True)
41+
42+
class Meta:
43+
model = ManyToManySource
44+
fields = ("targets",)

Diff for: tests/test_relations.py

+160-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import pytest
22
from django.conf.urls import re_path
3+
from rest_framework import status
34
from rest_framework.routers import SimpleRouter
45

6+
from rest_framework_json_api.exceptions import Conflict
57
from rest_framework_json_api.relations import HyperlinkedRelatedField
68
from rest_framework_json_api.views import ModelViewSet, RelationshipView
7-
8-
from .models import BasicModel
9+
from tests.models import BasicModel
10+
from tests.serializers import (
11+
ForeignKeySourceSerializer,
12+
ManyToManySourceReadOnlySerializer,
13+
ManyToManySourceSerializer,
14+
)
915

1016

1117
@pytest.mark.urls(__name__)
@@ -43,6 +49,158 @@ def test_relationship_urls_respect_format_related_links_setting(
4349
assert expected == actual
4450

4551

52+
@pytest.mark.django_db
53+
class TestResourceRelatedField:
54+
@pytest.mark.parametrize(
55+
"format_type,pluralize_type,resource_type",
56+
[
57+
(False, False, "ForeignKeyTarget"),
58+
(False, True, "ForeignKeyTargets"),
59+
("dasherize", False, "foreign-key-target"),
60+
("dasherize", True, "foreign-key-targets"),
61+
],
62+
)
63+
def test_serialize(
64+
self, format_type, pluralize_type, resource_type, foreign_key_target, settings
65+
):
66+
settings.JSON_API_FORMAT_TYPES = format_type
67+
settings.JSON_API_PLURALIZE_TYPES = pluralize_type
68+
69+
serializer = ForeignKeySourceSerializer(instance={"target": foreign_key_target})
70+
expected = {
71+
"type": resource_type,
72+
"id": str(foreign_key_target.pk),
73+
}
74+
75+
assert serializer.data["target"] == expected
76+
77+
@pytest.mark.parametrize(
78+
"format_type,pluralize_type,resource_type",
79+
[
80+
(False, False, "ForeignKeyTarget"),
81+
(False, True, "ForeignKeyTargets"),
82+
("dasherize", False, "foreign-key-target"),
83+
("dasherize", True, "foreign-key-targets"),
84+
],
85+
)
86+
def test_deserialize(
87+
self, format_type, pluralize_type, resource_type, foreign_key_target, settings
88+
):
89+
settings.JSON_API_FORMAT_TYPES = format_type
90+
settings.JSON_API_PLURALIZE_TYPES = pluralize_type
91+
92+
serializer = ForeignKeySourceSerializer(
93+
data={"target": {"type": resource_type, "id": str(foreign_key_target.pk)}}
94+
)
95+
96+
assert serializer.is_valid()
97+
assert serializer.validated_data["target"] == foreign_key_target
98+
99+
@pytest.mark.parametrize(
100+
"format_type,pluralize_type,resource_type",
101+
[
102+
(False, False, "ForeignKeyTargets"),
103+
(False, False, "Invalid"),
104+
(False, False, "foreign-key-target"),
105+
(False, True, "ForeignKeyTarget"),
106+
("dasherize", False, "ForeignKeyTarget"),
107+
("dasherize", True, "ForeignKeyTargets"),
108+
],
109+
)
110+
def test_validation_fails_on_invalid_type(
111+
self, format_type, pluralize_type, resource_type, foreign_key_target, settings
112+
):
113+
settings.JSON_API_FORMAT_TYPES = format_type
114+
settings.JSON_API_PLURALIZE_TYPES = pluralize_type
115+
116+
with pytest.raises(Conflict) as e:
117+
serializer = ForeignKeySourceSerializer(
118+
data={
119+
"target": {"type": resource_type, "id": str(foreign_key_target.pk)}
120+
}
121+
)
122+
serializer.is_valid()
123+
assert e.value.status_code == status.HTTP_409_CONFLICT
124+
125+
@pytest.mark.parametrize(
126+
"format_type,pluralize_type,resource_type",
127+
[
128+
(False, False, "ManyToManyTarget"),
129+
(False, True, "ManyToManyTargets"),
130+
("dasherize", False, "many-to-many-target"),
131+
("dasherize", True, "many-to-many-targets"),
132+
],
133+
)
134+
def test_serialize_many_to_many_relation(
135+
self,
136+
format_type,
137+
pluralize_type,
138+
resource_type,
139+
many_to_many_source,
140+
many_to_many_targets,
141+
settings,
142+
):
143+
settings.JSON_API_FORMAT_TYPES = format_type
144+
settings.JSON_API_PLURALIZE_TYPES = pluralize_type
145+
146+
serializer = ManyToManySourceSerializer(instance=many_to_many_source)
147+
expected = [
148+
{"type": resource_type, "id": str(target.pk)}
149+
for target in many_to_many_targets
150+
]
151+
assert serializer.data["targets"] == expected
152+
153+
@pytest.mark.parametrize(
154+
"format_type,pluralize_type,resource_type",
155+
[
156+
(False, False, "ManyToManyTarget"),
157+
(False, True, "ManyToManyTargets"),
158+
("dasherize", False, "many-to-many-target"),
159+
("dasherize", True, "many-to-many-targets"),
160+
],
161+
)
162+
@pytest.mark.parametrize(
163+
"serializer_class",
164+
[ManyToManySourceSerializer, ManyToManySourceReadOnlySerializer],
165+
)
166+
def test_deserialize_many_to_many_relation(
167+
self,
168+
format_type,
169+
pluralize_type,
170+
resource_type,
171+
serializer_class,
172+
many_to_many_targets,
173+
settings,
174+
):
175+
settings.JSON_API_FORMAT_TYPES = format_type
176+
settings.JSON_API_PLURALIZE_TYPES = pluralize_type
177+
178+
targets = [
179+
{"type": resource_type, "id": target.pk} for target in many_to_many_targets
180+
]
181+
serializer = ManyToManySourceSerializer(data={"targets": targets})
182+
assert serializer.is_valid()
183+
assert serializer.validated_data["targets"] == many_to_many_targets
184+
185+
@pytest.mark.parametrize(
186+
"resource_identifier,error",
187+
[
188+
(
189+
{"type": "ForeignKeyTarget"},
190+
"Invalid resource identifier object: missing 'id' attribute",
191+
),
192+
(
193+
{"id": "1234"},
194+
"Invalid resource identifier object: missing 'type' attribute",
195+
),
196+
],
197+
)
198+
def test_invalid_resource_id_object(self, resource_identifier, error):
199+
serializer = ForeignKeySourceSerializer(data={"target": resource_identifier})
200+
assert not serializer.is_valid()
201+
assert serializer.errors == {"target": [error]}
202+
203+
46204
# Routing setup
47205

48206

0 commit comments

Comments
 (0)