#25746: Isolate inlined test models
------------------------------------------------+------------------------
               Reporter:  charettes             |          Owner:  nobody
                   Type:  Cleanup/optimization  |         Status:  new
              Component:  Testing framework     |        Version:  master
               Severity:  Normal                |       Keywords:
           Triage Stage:  Unreviewed            |      Has patch:  0
    Needs documentation:  0                     |    Needs tests:  0
Patch needs improvement:  0                     |  Easy pickings:  0
                  UI/UX:  0                     |
------------------------------------------------+------------------------
 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.

--
Ticket URL: <https://code.djangoproject.com/ticket/25746>
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/052.92830947c9394315c54a5f83e925feb0%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to