Объекты меняются местами при выполнении тестов в django

Сразу хочу извиниться, если я что-то неправильно объяснил. Я пишу это с помощью google translate

Django показывает ошибку в тесте после одного теста. Тест проходит один раз, а затем терпит неудачу. Если я даже ничего не меняю, то один тест все равно проходит, а другой нет.

Тесты

def create_category():
    return Category.objects.create(category_title='Category title', slug='category-title')


def create_news(category, title, text='news text', pub_date=timezone.now()):
    return News.objects.create(category=category, image=None, title=title,
                               text=text, pub_date=pub_date)


class HomePageTestCase(TestCase):
    def test_no_news(self):
        response = self.client.get(reverse('news:home'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'News not found')
        self.assertQuerysetEqual(response.context['last_20_news'], [])

    def test_one_news(self):
        category = create_category()
        news = create_news(category, 'News title')
        response = self.client.get(reverse('news:home'))
        self.assertQuerysetEqual(response.context['last_20_news'], ['<News: News title>'])

    def test_multiple_news(self):
        category = create_category()
        news_1 = create_news(category, 'News title 1')
        news_2 = create_news(category, 'News title 2')
        response = self.client.get(reverse('news:home'))
        self.assertQuerysetEqual(response.context['last_20_news'], ['<News: News title 2>', '<News: News title 1>'])


class CategoryPageTestCase(TestCase):
    def test_no_news_from_category(self):
        category = create_category()
        response = self.client.get(category.get_absolute_url())
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'News not found')
        self.assertQuerysetEqual(response.context['news_from_category'], [])

    def test_one_news_from_category(self):
        category = create_category()
        news = create_news(category, 'News title')
        response = self.client.get(category.get_absolute_url())
        self.assertQuerysetEqual(response.context['news_from_category'], ['<News: News title>'])

    def test_multiple_news_from_category(self):
        category = create_category()
        news_1 = create_news(category, 'News title 1')
        news_2 = create_news(category, 'News title 2')
        response = self.client.get(category.get_absolute_url())
        self.assertQuerysetEqual(response.context['news_from_category'], ['<News: News title 2>', '<News: News title 1>'])

Виды, для которых были созданы тесты

class HomePageView(ListView):
    template_name = 'index.html'
    context_object_name = 'last_20_news'

    def get_queryset(self):
        return News.objects.order_by('-pub_date')[:20].select_related('category')

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(HomePageView, self).get_context_data()
        context['categories'] = Category.objects.all()
        return context


class NewsFromCategoryView(ListView):
    template_name = 'news/category_page.html'
    slug_url_kwarg = 'category_slug'
    paginate_by = 20
    context_object_name = 'news_from_category'

    def get_queryset(self):
        return News.objects.filter(category__slug=self.kwargs['category_slug']).select_related('category')

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super(NewsFromCategoryView, self).get_context_data()
        context['category'] = Category.objects.get(slug=self.kwargs['category_slug'])
        return context

Результат из консоли

λ python manage.py test news

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F..F..
======================================================================
FAIL: test_multiple_news_from_category (news.tests.CategoryPageTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Projects\project\news\tests.py", line 59, in test_multiple_news_from_category
    self.assertQuerysetEqual(response.context['news_from_category'], ['<News: News title 2>', '<News: News title 1>'])
  File "C:\Users\zepte\.virtualenvs\project-a8cUl3-w\lib\site-packages\django\test\testcases.py", line 1072, in assertQuerysetEqual
    return self.assertEqual(list(items), values, msg=msg)
AssertionError: Lists differ: ['<News: News title 1>', '<News: News title 2>'] != ['<News: News title 2>', '<News: News title 1>']

First differing element 0:
'<News: News title 1>'
'<News: News title 2>'

- ['<News: News title 1>', '<News: News title 2>']
?                     ^                       ^

+ ['<News: News title 2>', '<News: News title 1>']
?                     ^                       ^


======================================================================
FAIL: test_multiple_news (news.tests.HomePageTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Projects\project\news\tests.py", line 37, in test_multiple_news
    self.assertQuerysetEqual(response.context['last_20_news'], ['<News: News title 2>', '<News: News title 1>'])
  File "C:\Users\zepte\.virtualenvs\project-a8cUl3-w\lib\site-packages\django\test\testcases.py", line 1072, in assertQuerysetEqual
    return self.assertEqual(list(items), values, msg=msg)
AssertionError: Lists differ: ['<News: News title 1>', '<News: News title 2>'] != ['<News: News title 2>', '<News: News title 1>']

First differing element 0:
'<News: News title 1>'
'<News: News title 2>'

- ['<News: News title 1>', '<News: News title 2>']
?                     ^                       ^

+ ['<News: News title 2>', '<News: News title 1>']
?                     ^                       ^


----------------------------------------------------------------------
Ran 6 tests in 0.272s

FAILED (failures=2)
Destroying test database for alias 'default'...
<
λ python manage.py test news

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
......
----------------------------------------------------------------------
Ran 6 tests in 0.354s

OK
Destroying test database for alias 'default'...

Результат, а именно объекты ( ['<News: News title 2>', '<News: News title 1>'] ) в тестах (test_multiple_news и test_multiple_news_from_category) меняются местами каждый раз, когда я запускаю тест, и я хочу знать, почему это происходит.

Используя:

def create_news(category, title, text='news text', pub_date=timezone.now()):
    return News.objects.create(category=category, image=None, title=title, text=text, pub_date=pub_date)

оба предмета будут иметь одинаковый pub_date. Вы должны использовать:

def create_news(category, title, text='news text', pub_date=None):
    if pub_date is not None:
        pub_date = timezone.now()
    return News.objects.create(category=category, image=None, title=title, text=text, pub_date=pub_date)

Но все же возможно, что два элемента вставляются настолько быстро, что имеют одинаковую метку времени. Поэтому лучше воспользоваться таким инструментом, как freezegun [GitHub], чтобы убедиться, что оба элемента создаются с промежутком хотя бы в несколько миллисекунд:

from freezegun import freeze_time

class HomePageTestCase(TestCase):
    # …

    @freeze_time('2021-12-26', auto_tick_seconds=15)
    def test_multiple_news(self):
        category = create_category()
        news_1 = create_news(category, 'News title 1')
        news_2 = create_news(category, 'News title 2')
        # …

Вы должны применять ту же логику к элементам, где вы делаете множество вставок близко друг к другу.

Вернуться на верх