Skip to content

Commit 8064ef6

Browse files
committed
Reset sequences in a project-wide fixture and revert most changes
1 parent 6b361df commit 8064ef6

10 files changed

Lines changed: 386 additions & 420 deletions

tests/conftest.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,36 @@
33
import dj_database_url
44
import django
55
import pytest
6+
from django.apps import apps
67
from django.core import management
8+
from django.core.management.color import no_style
9+
from django.db import connection
10+
from django.test import TestCase, TransactionTestCase
11+
12+
13+
@pytest.fixture(autouse=True)
14+
def _reset_sequences(request):
15+
"""Reset all database sequences so PKs start from 1 in each test.
16+
17+
PostgreSQL sequences are non-transactional and persist across
18+
TestCase's transaction rollbacks. This fixture ensures every test
19+
gets predictable PKs starting from 1 regardless of execution order.
20+
No-op on SQLite and skipped for tests that don't use the database.
21+
"""
22+
if connection.vendor != 'postgresql':
23+
return
24+
# Only run for tests that actually have database access.
25+
if not (request.cls and issubclass(request.cls, (TestCase, TransactionTestCase))):
26+
if 'db' not in request.fixturenames and 'transactional_db' not in request.fixturenames:
27+
return
28+
29+
table_names = set(connection.introspection.table_names())
30+
models = [m for m in apps.get_models() if m._meta.db_table in table_names]
31+
sql_list = connection.ops.sequence_reset_sql(no_style(), models)
32+
if sql_list:
33+
with connection.cursor() as cursor:
34+
for sql in sql_list:
35+
cursor.execute(sql)
736

837

938
def pytest_addoption(parser):

tests/test_filters.py

Lines changed: 66 additions & 73 deletions
Large diffs are not rendered by default.

tests/test_generics.py

Lines changed: 42 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@ def test_post_root_view(self):
119119
with self.assertNumQueries(1):
120120
response = self.view(request).render()
121121
assert response.status_code == status.HTTP_201_CREATED
122-
created = self.objects.get(text='foobar')
123-
assert response.data == {'id': created.pk, 'text': 'foobar'}
122+
assert response.data == {'id': 4, 'text': 'foobar'}
123+
created = self.objects.get(id=4)
124+
assert created.text == 'foobar'
124125

125126
def test_put_root_view(self):
126127
"""
@@ -152,8 +153,9 @@ def test_post_cannot_set_id(self):
152153
with self.assertNumQueries(1):
153154
response = self.view(request).render()
154155
assert response.status_code == status.HTTP_201_CREATED
155-
created = self.objects.get(text='foobar')
156-
assert response.data == {'id': created.pk, 'text': 'foobar'}
156+
assert response.data == {'id': 4, 'text': 'foobar'}
157+
created = self.objects.get(id=4)
158+
assert created.text == 'foobar'
157159

158160
def test_post_error_root_view(self):
159161
"""
@@ -175,11 +177,8 @@ def setUp(self):
175177
Create 3 BasicModel instances.
176178
"""
177179
items = ['foo', 'bar', 'baz', 'filtered out']
178-
self.created_items = []
179180
for item in items:
180-
obj = BasicModel.objects.create(text=item)
181-
if item != 'filtered out':
182-
self.created_items.append(obj)
181+
BasicModel(text=item).save()
183182
self.objects = BasicModel.objects.exclude(text='filtered out')
184183
self.data = [
185184
{'id': obj.id, 'text': obj.text}
@@ -192,10 +191,9 @@ def test_get_instance_view(self):
192191
"""
193192
GET requests to RetrieveUpdateDestroyAPIView should return a single object.
194193
"""
195-
pk = self.created_items[0].pk
196-
request = factory.get(f'/{pk}')
194+
request = factory.get('/1')
197195
with self.assertNumQueries(1):
198-
response = self.view(request, pk=pk).render()
196+
response = self.view(request, pk=1).render()
199197
assert response.status_code == status.HTTP_200_OK
200198
assert response.data == self.data[0]
201199

@@ -214,43 +212,40 @@ def test_put_instance_view(self):
214212
"""
215213
PUT requests to RetrieveUpdateDestroyAPIView should update an object.
216214
"""
217-
pk = self.created_items[0].pk
218215
data = {'text': 'foobar'}
219-
request = factory.put(f'/{pk}', data, format='json')
216+
request = factory.put('/1', data, format='json')
220217
with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT):
221-
response = self.view(request, pk=str(pk)).render()
218+
response = self.view(request, pk='1').render()
222219
assert response.status_code == status.HTTP_200_OK
223-
assert dict(response.data) == {'id': pk, 'text': 'foobar'}
224-
updated = self.objects.get(id=pk)
220+
assert dict(response.data) == {'id': 1, 'text': 'foobar'}
221+
updated = self.objects.get(id=1)
225222
assert updated.text == 'foobar'
226223

227224
def test_patch_instance_view(self):
228225
"""
229226
PATCH requests to RetrieveUpdateDestroyAPIView should update an object.
230227
"""
231-
pk = self.created_items[0].pk
232228
data = {'text': 'foobar'}
233-
request = factory.patch(f'/{pk}', data, format='json')
229+
request = factory.patch('/1', data, format='json')
234230

235231
with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT):
236-
response = self.view(request, pk=pk).render()
232+
response = self.view(request, pk=1).render()
237233
assert response.status_code == status.HTTP_200_OK
238-
assert response.data == {'id': pk, 'text': 'foobar'}
239-
updated = self.objects.get(id=pk)
234+
assert response.data == {'id': 1, 'text': 'foobar'}
235+
updated = self.objects.get(id=1)
240236
assert updated.text == 'foobar'
241237

242238
def test_delete_instance_view(self):
243239
"""
244240
DELETE requests to RetrieveUpdateDestroyAPIView should delete an object.
245241
"""
246-
pk = self.created_items[0].pk
247-
request = factory.delete(f'/{pk}')
242+
request = factory.delete('/1')
248243
with self.assertNumQueries(2):
249-
response = self.view(request, pk=pk).render()
244+
response = self.view(request, pk=1).render()
250245
assert response.status_code == status.HTTP_204_NO_CONTENT
251246
assert response.content == b''
252247
ids = [obj.id for obj in self.objects.all()]
253-
assert ids == [self.created_items[1].pk, self.created_items[2].pk]
248+
assert ids == [2, 3]
254249

255250
def test_get_instance_view_incorrect_arg(self):
256251
"""
@@ -266,27 +261,25 @@ def test_put_cannot_set_id(self):
266261
"""
267262
PUT requests to create a new object should not be able to set the id.
268263
"""
269-
pk = self.created_items[0].pk
270264
data = {'id': 999, 'text': 'foobar'}
271-
request = factory.put(f'/{pk}', data, format='json')
265+
request = factory.put('/1', data, format='json')
272266
with self.assertNumQueries(EXPECTED_QUERIES_FOR_PUT):
273-
response = self.view(request, pk=pk).render()
267+
response = self.view(request, pk=1).render()
274268
assert response.status_code == status.HTTP_200_OK
275-
assert response.data == {'id': pk, 'text': 'foobar'}
276-
updated = self.objects.get(id=pk)
269+
assert response.data == {'id': 1, 'text': 'foobar'}
270+
updated = self.objects.get(id=1)
277271
assert updated.text == 'foobar'
278272

279273
def test_put_to_deleted_instance(self):
280274
"""
281275
PUT requests to RetrieveUpdateDestroyAPIView should return 404 if
282276
an object does not currently exist.
283277
"""
284-
pk = self.created_items[0].pk
285-
self.objects.get(id=pk).delete()
278+
self.objects.get(id=1).delete()
286279
data = {'text': 'foobar'}
287-
request = factory.put(f'/{pk}', data, format='json')
280+
request = factory.put('/1', data, format='json')
288281
with self.assertNumQueries(1):
289-
response = self.view(request, pk=pk).render()
282+
response = self.view(request, pk=1).render()
290283
assert response.status_code == status.HTTP_404_NOT_FOUND
291284

292285
def test_put_to_filtered_out_instance(self):
@@ -305,21 +298,19 @@ def test_patch_cannot_create_an_object(self):
305298
PATCH requests should not be able to create objects.
306299
"""
307300
data = {'text': 'foobar'}
308-
non_existent_pk = 999999
309-
request = factory.patch(f'/{non_existent_pk}', data, format='json')
301+
request = factory.patch('/999', data, format='json')
310302
with self.assertNumQueries(1):
311-
response = self.view(request, pk=non_existent_pk).render()
303+
response = self.view(request, pk=999).render()
312304
assert response.status_code == status.HTTP_404_NOT_FOUND
313-
assert not self.objects.filter(id=non_existent_pk).exists()
305+
assert not self.objects.filter(id=999).exists()
314306

315307
def test_put_error_instance_view(self):
316308
"""
317309
Incorrect PUT requests in HTML should include a form error.
318310
"""
319-
pk = self.created_items[0].pk
320311
data = {'text': 'foobar' * 100}
321312
request = factory.put('/', data, HTTP_ACCEPT='text/html')
322-
response = self.view(request, pk=pk).render()
313+
response = self.view(request, pk=1).render()
323314
expected_error = '<span class="help-block">Ensure this field has no more than 100 characters.</span>'
324315
assert expected_error in response.rendered_content.decode()
325316

@@ -354,10 +345,8 @@ def setUp(self):
354345
Create 3 BasicModel instances.
355346
"""
356347
items = ['foo', 'bar', 'baz']
357-
self.created_items = []
358348
for item in items:
359-
obj = BasicModel.objects.create(text=item)
360-
self.created_items.append(obj)
349+
BasicModel(text=item).save()
361350
self.objects = BasicModel.objects
362351
self.data = [
363352
{'id': obj.id, 'text': obj.text}
@@ -380,10 +369,9 @@ def test_overridden_get_object_view(self):
380369
"""
381370
GET requests to RetrieveUpdateDestroyAPIView should return a single object.
382371
"""
383-
pk = self.created_items[0].pk
384-
request = factory.get(f'/{pk}')
372+
request = factory.get('/1')
385373
with self.assertNumQueries(1):
386-
response = self.view(request, pk=pk).render()
374+
response = self.view(request, pk=1).render()
387375
assert response.status_code == status.HTTP_200_OK
388376
assert response.data == self.data[0]
389377

@@ -416,7 +404,7 @@ def test_create_model_with_auto_now_add_field(self):
416404
request = factory.post('/', data, format='json')
417405
response = self.view(request).render()
418406
assert response.status_code == status.HTTP_201_CREATED
419-
created = self.objects.get(content='foobar')
407+
created = self.objects.get(id=1)
420408
assert created.content == 'foobar'
421409

422410

@@ -495,10 +483,8 @@ def setUp(self):
495483
Create 3 BasicModel instances to filter on.
496484
"""
497485
items = ['foo', 'bar', 'baz']
498-
self.created_items = []
499486
for item in items:
500-
obj = BasicModel.objects.create(text=item)
501-
self.created_items.append(obj)
487+
BasicModel(text=item).save()
502488
self.objects = BasicModel.objects
503489
self.data = [
504490
{'id': obj.id, 'text': obj.text}
@@ -514,8 +500,7 @@ def test_get_root_view_filters_by_name_with_filter_backend(self):
514500
response = root_view(request).render()
515501
assert response.status_code == status.HTTP_200_OK
516502
assert len(response.data) == 1
517-
foo_obj = self.created_items[0]
518-
assert response.data == [{'id': foo_obj.pk, 'text': 'foo'}]
503+
assert response.data == [{'id': 1, 'text': 'foo'}]
519504

520505
def test_get_root_view_filters_out_all_models_with_exclusive_filter_backend(self):
521506
"""
@@ -531,10 +516,9 @@ def test_get_instance_view_filters_out_name_with_filter_backend(self):
531516
"""
532517
GET requests to RetrieveUpdateDestroyAPIView should raise 404 when model filtered out.
533518
"""
534-
pk = self.created_items[0].pk
535519
instance_view = InstanceView.as_view(filter_backends=(ExclusiveFilterBackend,))
536-
request = factory.get(f'/{pk}')
537-
response = instance_view(request, pk=pk).render()
520+
request = factory.get('/1')
521+
response = instance_view(request, pk=1).render()
538522
assert response.status_code == status.HTTP_404_NOT_FOUND
539523
assert response.data == {
540524
'detail': ErrorDetail(
@@ -547,12 +531,11 @@ def test_get_instance_view_will_return_single_object_when_filter_does_not_exclud
547531
"""
548532
GET requests to RetrieveUpdateDestroyAPIView should return a single object when not excluded
549533
"""
550-
foo_obj = self.created_items[0]
551534
instance_view = InstanceView.as_view(filter_backends=(InclusiveFilterBackend,))
552-
request = factory.get(f'/{foo_obj.pk}')
553-
response = instance_view(request, pk=foo_obj.pk).render()
535+
request = factory.get('/1')
536+
response = instance_view(request, pk=1).render()
554537
assert response.status_code == status.HTTP_200_OK
555-
assert response.data == {'id': foo_obj.pk, 'text': 'foo'}
538+
assert response.data == {'id': 1, 'text': 'foo'}
556539

557540
def test_dynamic_serializer_form_in_browsable_api(self):
558541
"""

tests/test_model_serializer.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ class DisplayValueModel(models.Model):
760760

761761
class TestRelationalFieldDisplayValue(TestCase):
762762
def setUp(self):
763-
self.targets = DisplayValueTargetModel.objects.bulk_create([
763+
DisplayValueTargetModel.objects.bulk_create([
764764
DisplayValueTargetModel(name='Red'),
765765
DisplayValueTargetModel(name='Yellow'),
766766
DisplayValueTargetModel(name='Green'),
@@ -773,7 +773,7 @@ class Meta:
773773
fields = '__all__'
774774

775775
serializer = TestSerializer()
776-
expected = {t.pk: '%s Color' % t.name for t in self.targets}
776+
expected = {1: 'Red Color', 2: 'Yellow Color', 3: 'Green Color'}
777777
self.assertEqual(serializer.fields['color'].choices, expected)
778778

779779
def test_custom_display_value(self):
@@ -789,7 +789,7 @@ class Meta:
789789
fields = '__all__'
790790

791791
serializer = TestSerializer()
792-
expected = {t.pk: 'My %s Color' % t.name for t in self.targets}
792+
expected = {1: 'My Red Color', 2: 'My Yellow Color', 3: 'My Green Color'}
793793
self.assertEqual(serializer.fields['color'].choices, expected)
794794

795795

@@ -1278,10 +1278,10 @@ class Meta:
12781278
parent_serializer = TestParentModelSerializer(parent)
12791279
child_serializer = TestChildModelSerializer(child)
12801280

1281-
parent_expected = {'children': ['def'], 'id': parent.pk, 'title': 'abc'}
1281+
parent_expected = {'children': ['def'], 'id': 1, 'title': 'abc'}
12821282
self.assertEqual(parent_serializer.data, parent_expected)
12831283

1284-
child_expected = {'parent': parent.pk, 'value': 'def'}
1284+
child_expected = {'parent': 1, 'value': 'def'}
12851285
self.assertEqual(child_serializer.data, child_expected)
12861286

12871287

0 commit comments

Comments
 (0)