I have run into this problem myself in the past. On a previous project we
added a helper function to make deepcopy's of named attributes during
setUp().

>From a check against a few projects and Django's test suite[2] I have only
> identified a single issue which is that attributes assigned during
> `setUpTestData` would now have to be `deepcopy()`able but it shouldn't be
> a blocker given `Model` instance are.


I think this is too much of an ask for backwards compatibility. Lots of
things aren't deepcopy-able, as per its docs:

This module does not copy types like module, method, stack trace, stack
> frame, file, socket, window, array, or any similar types. It does “copy”
> functions and classes (shallow and deeply), by returning the original
> object unchanged; this is compatible with the way these are treated by the
> pickle module.


How about adding a container object to TestCase, which deepcopy()'s its
attributes on setUp. It could be called something short like "data', which
would make your example:

class BookTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.data.author = Author.objects.create()
        cls.data.book = cls.author.books.create()

    def test_relationship_preserved(self):
        self.assertIs(self.data.book.author, self.data.author)

On Sat, 24 Nov 2018 at 03:29, charettes <charett...@gmail.com> wrote:

> Dear developers,
>
> Django 1.8 introduced the `TestCase.setUpTestData()` class method as a
> mean to
> speed up test fixtures initialization as compared to using `setUp()`[0].
>
> As I've come to use this feature and review changes from peers using it in
> different projects the fact that test data assigned during its execution
> couldn't be safely altered by test methods without compromising test
> isolation
> has often be the source of confusion and frustration.
>
> While the `setUpTestData` documentation mentions this limitation[1] and
> ways to
> work around it by using `refresh_from_db()` in `setUp()` I believe it
> defeats
> the whole purpose of the feature; avoiding unnecessary roundtrips to the
> database to speed up execution. Given `TestCase` goes through great
> lengths to
> ensure database level data isolation I believe it should do the same with
> class
> level in-memory data assigned during `setUpTestData`.
>
> In order to get rid of this caveat of the feature I'd like to propose an
> adjustment to ensure such in-memory test data isolation.
>
> What I suggest doing is wrapping all attributes assigned during
> `setUpTestData`
> in descriptors that lazily return `copy.deepcopy()`ed values on instance
> attribute accesses. By attaching the `deepcopy()`'s memo on test instances
> we
> can ensure that the reference graph between objects is preserved and thus
> backward compatible.
>
> In other words, the following test would pass even if `self.book` is a deep
> copy of `cls.book`.
>
> class BookTests(TestCase):
>     @classmethod
>     def setUpTestData(cls):
>         cls.author = Author.objects.create()
>         cls.book = cls.author.books.create()
>
>     def test_relationship_preserved(self):
>         self.assertIs(self.book.author, self.author)
>
> Lazily returning `deepcopy'ies and caching returned values in `__dict__` à
> la
> `cached_property` should also make sure the slight performance overhead
> this
> incurs is minimized.
>
> From a check against a few projects and Django's test suite[2] I have only
> identified a single issue which is that attributes assigned during
> `setUpTestData` would now have to be `deepcopy()`able but it shouldn't be
> a blocker given `Model` instance are.
>
> In order to allow other possible issues from being identified against
> existing
> projects I packaged the proposed feature[3] and made it available on
> pypi[4]. It
> requires decorating `setUpTestData` methods but it shouldn't be too hard to
> apply to your projects if you want to give it a try.
>
> Given this reaches consensus that this could be a great addition I'd file
> a ticket and finalize what I have so far[2].
>
> Thank your for your time,
> Simon
>
> [0]
> https://docs.djangoproject.com/en/1.8/releases/1.8/#testcase-data-setup
> [1]
> https://docs.djangoproject.com/en/2.1/topics/testing/tools/#django.test.TestCase.setUpTestData
> [2]
> https://github.com/charettes/django/compare/setuptestdata...charettes:testdata
> [3] https://github.com/charettes/django-testdata
> [4] https://pypi.org/project/django-testdata/
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> Visit this group at https://groups.google.com/group/django-developers.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/7563b4b6-2708-4614-a74a-2b63ecad2f67%40googlegroups.com
> <https://groups.google.com/d/msgid/django-developers/7563b4b6-2708-4614-a74a-2b63ecad2f67%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>


-- 
Adam

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CAMyDDM3WKMLkC3iL%3DorGA_Eop%3DUqA-vhDQOg58Js4DVGx-HAVw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to