Skip to content

Commit d466743

Browse files
author
Jakub Owczarski
committed
created django_use_model marker for testing unmanaged models
1 parent b9eb210 commit d466743

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

docs/helpers.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@ on what marks are and for notes on using_ them.
9494
client('some-url-with-invalid-template-vars')
9595

9696

97+
``pytest.mark.django_use_model`` - force model creation for unmanaged models
98+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
99+
100+
.. py:function:: pytest.mark.django_use_model(model)
101+
102+
:type model: django model or list of django models
103+
:param model:
104+
Model or models to be created, should be used only with models that
105+
have ``Meta.managed = False``
106+
107+
This will create requested model(s) for the scope of the marker.
108+
Allows testing of unmanaged models that are normally not created.
109+
110+
.. note::
111+
112+
To access database you still have to request access by using
113+
``pytest.mark.django_db``
114+
115+
Example usage::
116+
117+
@pytest.mark.django_db
118+
@pytest.mark.django_use_model(Unmanaged)
119+
def test_unmanaged():
120+
assert Unmanaged.objects.count() >= 0
121+
122+
97123
Fixtures
98124
--------
99125

pytest_django/plugin.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ def pytest_load_initial_conftests(early_config, parser, args):
168168
'the `urls` attribute of Django `TestCase` objects. *modstr* is '
169169
'a string specifying the module of a URL config, e.g. '
170170
'"my_app.test_urls".')
171+
early_config.addinivalue_line(
172+
'markers',
173+
'django_use_model(model): force model creation, '
174+
'even for unmanaged models. Model(s) are deleted at the end of scope')
171175

172176
options = parser.parse_known_args(args)
173177

@@ -297,6 +301,48 @@ def _django_db_marker(request):
297301
request.getfuncargvalue('db')
298302

299303

304+
@pytest.fixture(autouse=True)
305+
def _django_use_model(request):
306+
"""Implement ``django_use_model`` marker.
307+
308+
Marker creates unmanaged models that normally aren't created.
309+
Destroys it at the end of marked scope.
310+
311+
Note that you still need to use ``django_db`` marker before this one.
312+
The test unit should be decorated:
313+
314+
@pytest.mark.django_db
315+
@pytest.mark.django_use_model(model)
316+
317+
:model: ModelClass, one or many
318+
"""
319+
marker = request.keywords.get('django_use_model', None)
320+
if not marker:
321+
return
322+
from django.db import connection
323+
324+
model = request.getfuncargvalue('model')
325+
326+
if isinstance(model, (list, tuple)):
327+
models = model
328+
else:
329+
models = (model,)
330+
331+
with contextlib.closing(connection.schema_editor()) as schema:
332+
schema.deferred_sql = []
333+
for model_class in models:
334+
if not hasattr(model, '_meta'):
335+
raise ValueError('"model" must be a valid model class')
336+
schema.create_model(model_class)
337+
338+
def drop():
339+
with contextlib.closing(connection.schema_editor()) as schema:
340+
for model_class in models:
341+
schema.delete_model(model_class)
342+
343+
request.addfinalizer(drop)
344+
345+
300346
@pytest.fixture(autouse=True, scope='class')
301347
def _django_setup_unittest(request, _django_cursor_wrapper):
302348
"""Setup a django unittest, internal to pytest-django."""

pytest_django_test/app/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,10 @@
33

44
class Item(models.Model):
55
name = models.CharField(max_length=100)
6+
7+
8+
class Unmanaged(models.Model):
9+
name = models.CharField(max_length=100)
10+
11+
class Meta:
12+
managed = False

tests/test_database.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from __future__ import with_statement
22

33
import pytest
4-
from django.db import connection, transaction
4+
from django.db import connection, transaction, DatabaseError
55
from django.test.testcases import connections_support_transactions
66

7-
from pytest_django_test.app.models import Item
7+
from pytest_django_test.app.models import Item, Unmanaged
88

99

1010
def noop_transactions():
@@ -164,6 +164,28 @@ def test_transactions_enabled(self):
164164
assert not noop_transactions()
165165

166166

167+
@pytest.mark.django_db
168+
class TestUseModel:
169+
"""Tests for django_use_model marker"""
170+
171+
def test_unmanaged_missing(self):
172+
"""Test that Unmanaged model is not created by default"""
173+
with pytest.raises(DatabaseError):
174+
# If table does not exists, django will raise DatabaseError
175+
# but the message will depend on the backend.
176+
# Probably nothing else can be asserted here.
177+
Unmanaged.objects.exists()
178+
179+
@pytest.mark.django_use_model(Unmanaged)
180+
def test_unmanaged_created(self):
181+
"""Make sure unmanaged models are created"""
182+
assert Unmanaged.objects.count() == 0
183+
184+
def test_unmanaged_destroyed(self):
185+
"""Test that Unmanaged model was destroyed after last use"""
186+
self.test_unmanaged_missing()
187+
188+
167189
def test_unittest_interaction(django_testdir):
168190
"Test that (non-Django) unittests cannot access the DB."
169191

0 commit comments

Comments
 (0)