Update to Django 1.8 - AttributeError: django.test.TestCase has no attribute 'cls_atomics'
Asked Answered
M

4

90

I updated a Django 1.7 project to Django 1.8 and now get errors when I run the tests (that are subclasses of django.test.TestCase).

Traceback (most recent call last):
  File "env\lib\site-packages\django\test\testcases.py", line 962, in tearDownClass
cls._rollback_atomics(cls.cls_atomics)
  AttributeError: type object 'SomeTests' has no attribute 'cls_atomics'

If I debug through the test I can step through all lines without problems, but after the last line the exception is thrown.

This is an example test:

import django
import unittest
from django.test import TestCase
import logging
import sys
from builtins import classmethod, isinstance

class ATestTests(TestCase):

    @classmethod
    def setUpClass(cls):
        django.setup()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


    def setUp(self):
        self._app = Application(name="a")


    def testtest(self):

        self.assertIsNotNone(self._app)

My environment:

astroid==1.3.4
colorama==0.3.3
defusedxml==0.4.1
Django==1.8
django-extensions==1.5.2
django-filter==0.9.2
djangorestframework==3.0.5
djangorestframework-xml==1.0.1
eight==0.3.0
future==0.11.4
logilab-common==0.63.2
Markdown==2.5.2
pylint==1.4.1
python-dateutil==2.4.1
python-mimeparse==0.1.4
six==1.9.0
xmltodict==0.9.2

How can I fix this?

Mb answered 15/4, 2015 at 14:34 Comment(8)
Can you paste here a pip freeze?Jihad
@Jihad done thatMb
mmm can I see the test implementation you run?Jihad
@Jihad I added more detailsMb
Do you still need this? "# Django 1.7 requires an explicit setup() when running tests in PTVS"Jihad
Is the full traceback crashing in the setup or the testtest()?Jihad
@Jihad the line if django.VERSION[:2] >= (1, 7): is not longer needed.Mb
I believe this answer from seddonym is the correct answer.Nitramine
S
47

For Django 1.8+, you should use TestCase.setUpTestData instead of TestCase.setUpClass.

class MyTests(TestCase):

    @classmethod
    def setUpTestData(cls):
        # Set up data for the whole TestCase
        cls.foo = Foo.objects.create(bar="Test")

    def test1(self):
        self.assertEqual(self.foo.bar, 'Test') 

The documentation is here.

Spotter answered 21/9, 2015 at 17:5 Comment(1)
Your comment resolves the error, but where in the documentation does it mention using TestCase.setUpTestData instead of TestCase.setUpClass for Django 1.8+?Putative
A
154

I believe the reason is that your setUpClass(cls) class method is not calling super. Because of that, django.tests.TestCase.setUpClass is not called and

cls.cls_atomics = cls._enter_atomics()

is not called, naturally causing cls_atomics to be undefined.

You should add super(ATestTests, cls).setUpClass() to your setUpClass.

Algar answered 15/4, 2015 at 16:6 Comment(3)
This is it. If I add the call to the base class it works again (did I mention that it worked with django 1.7 ...). I Also do not need the call to django.setup()any more.Mb
I thought i do not need django.setup() but i still need it in some cases, otherwise I get an exception that the model is not instantiated.Mb
See @seddonym's answer https://mcmap.net/q/212113/-update-to-django-1-8-attributeerror-django-test-testcase-has-no-attribute-39-cls_atomics-39 - you should use setUpTestData not setUpClass .Ursi
S
47

For Django 1.8+, you should use TestCase.setUpTestData instead of TestCase.setUpClass.

class MyTests(TestCase):

    @classmethod
    def setUpTestData(cls):
        # Set up data for the whole TestCase
        cls.foo = Foo.objects.create(bar="Test")

    def test1(self):
        self.assertEqual(self.foo.bar, 'Test') 

The documentation is here.

Spotter answered 21/9, 2015 at 17:5 Comment(1)
Your comment resolves the error, but where in the documentation does it mention using TestCase.setUpTestData instead of TestCase.setUpClass for Django 1.8+?Putative
V
7

I had a similar problem where a TestCase used setUpClass but did not have a tearDownClass method. My tests pass when I add an empty one:

@classmethod
def tearDownClass(cls):
    pass

I also do not call django.setup.

Vocational answered 29/5, 2015 at 7:36 Comment(3)
Following this answer could lead to problems with your teardown, where you have leftover objects and data that's otherwise not cleaned up. That can cause inconsistencies in your test suite, particularly if you run things in parallel.Semiconscious
@Semiconscious -- Yes, if you are creating any persistent objects, you should add some actual "cleanup" code to the teardown, I agree.Vocational
This avoids the the exception by stopping the call to the code causing the exception instead of actually fixing the problem. Basically you won't get proper automatic ROLLBACKs after each test.Unmade
M
-1

Here is the complete code with the call to the base class (as suggested by @J. C. Leitão):

import django
import unittest
from django.test import TestCase
import logging
import sys
from builtins import classmethod

class ATestTests(TestCase):

    @classmethod
    def setUpClass(cls):
        super(ATestTests, cls).setUpClass()
        django.setup()
        logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

    def setUp(self):
        self._app = Application(name="a")

    def testtest(self):

        self.assertIsNotNone(self._app)
Mb answered 16/4, 2015 at 11:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.