#25746: Isolate inlined test models
-------------------------------------+-------------------------------------
Reporter: charettes | Owner: charettes
Type: | Status: assigned
Cleanup/optimization |
Component: Testing framework | Version: master
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):
* stage: Unreviewed => Accepted
Old description:
> Many test in Django's own test suite inline model definition in order to
> prevent table creation or simply isolate them.
>
> Unfortunately most of these model don't explicitly declare an explicit
> `apps` and end up being registered to the global `apps` instance. This
> has three downsides:
>
> 1. The call to `register_model` busts the `apps` and registered models
> (1k+) `_meta` cache. I didn't run any benchmark here but I suspect this
> slow down things a bit.
> 2. If a model is already registered for the specified
> `app_label.ModelName` (see the `schema` issue detailed in #25745) a
> `RuntimeWarning` is raised.
> 3. If no model is registered for this particular `app_label.ModelName`
> any future call to `migrate` will end up creating the table since we
> don't have explicit migration defined for the test apps.
>
> To prevent the enumerated issues the inlined models should use a per-test
> isolated registry. Examples of such a pattern can be found on this
> [https://github.com/django/django/pull/5613 PR].
>
> e.g.
>
> {{{#!python
> def test_foo(self):
> test_apps = Apps(['test_app'])
> class Foo(models.Model):
> class Meta:
> apps = test_apps
> }}}
>
> In order to reduce the boiler plate required to achieve isolation I
> suggest introducing a test decorator/context manager that register models
> declared in it's body in an isolated `Apps()` instance.
>
> e.g.
>
> {{{#!python
> @isolate_apps('test_app', 'test_app_2')
> def test_foo(self):
> class Foo(models.Model):
> pass
>
> with isolate_apps('test_app3') as apps:
> class Bar(models.Model):
> class Meta:
> app_label = 'test_app3'
> assert Bar is apps.get_model('test_app3', 'Bar')
> }}}
>
> This would also make fixing the actual and future isolation problems
> quite easy by simply decorating the faulty test.
New description:
Many tests in Django's own test suite inline model definition in order to
prevent table creation or simply isolate them.
Unfortunately most of these model don't explicitly declare an explicit
`apps` and end up being registered to the global `apps` instance. This has
three downsides:
1. The call to `register_model` busts the `apps` and registered models
(1k+) `_meta` cache. I didn't run any benchmark here but I suspect this
slow down things a bit.
2. If a model is already registered for the specified
`app_label.ModelName` (see the `schema` issue detailed in #25745) a
`RuntimeWarning` is raised.
3. If no model is registered for this particular `app_label.ModelName` any
future call to `migrate` will end up creating the table since we don't
have explicit migration defined for the test apps.
To prevent the enumerated issues the inlined models should use a per-test
isolated registry. Examples of such a pattern can be found on this
[https://github.com/django/django/pull/5613 PR].
e.g.
{{{#!python
def test_foo(self):
test_apps = Apps(['test_app'])
class Foo(models.Model):
class Meta:
apps = test_apps
}}}
In order to reduce the boiler plate required to achieve isolation I
suggest introducing a test decorator/context manager that register models
declared in it's body in an isolated `Apps()` instance.
e.g.
{{{#!python
@isolate_apps('test_app', 'test_app_2')
def test_foo(self):
class Foo(models.Model):
pass
with isolate_apps('test_app3') as apps:
class Bar(models.Model):
class Meta:
app_label = 'test_app3'
assert Bar is apps.get_model('test_app3', 'Bar')
}}}
This would also make fixing the actual and future isolation problems quite
easy by simply decorating the faulty test.
--
--
Ticket URL: <https://code.djangoproject.com/ticket/25746#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/067.e353d3e45db019312510dfafbe925926%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.