I am writing tests for a webapp that is made in Python3.6/Django 2.0 and I have the following situation:
class TestFoo(TestCase):
def test_a(self):
obj = Foo.objects.create(a = "bar")
expectation = {"a" : "bar"}
self.assertEquals(obj.method_x(), expectation)
def test_b(self):
obj = Foo.objects.create(a = "baz")
expectation = {"a" : "baz"}
self.assertEquals(obj.method_x(), expectation)
def test_c(self):
obj = Foo.objects.create(a = "bar")
obj2 = Foo.objects.create(a = "baz")
obj.b.add(obj2)
expectation = {"a" : "bar", "b" : {"a": "baz"}}
self.assertEquals(obj.method_x(), expectation)
To my understanding every test is run in isolation, yet when I run test_c alongside test a or b, all tests fail. Basically this:
- test_a + test_b + test_c = ALL FAIL
- test_a + test_b = ALL PASS
- test_c = ALL PASS
- test_a + test_c = ALL FAIL
- test_b + test_c = ALL FAIL
I have tried:
- Deleting all Foo objects in teardown (in case this didn't happen), this had no effect
- Using patch.object, but this was not the behavior I desired because I want to test the method works correctly
- Sticking test_c in a separate class, this had no effect
- Running the tests in a certain order (a, then b, then c and first c, then a/b, this resulted in different points of failure; if I first run c, it passes, then a/b fail. If I run a/b first, then c fails
I am unsure what causes this behavior, but would like to resolve it; I know all tests by themselves should pass. I have been reading about mock/patch methods but I am pretty sure that this is not what I need because I need to verify that the method(s) of my object return valid data rather than ensure that they get called or anything like that.
So basically my question is twofold:
- Why is this happening?
- How do I prevent it?
Edit 1: The assertion error traceback is pretty weird too; obviously the values do not equal each other, but what's more is that the values are somehow mixed up. Somehow test_a.method_x() == test_c.method_x(), but test_a.a =/= test_c.a
method_x is something like:
def method_x(self):
if not self.b:
return {"a": self.a}
else:
return {"a": self.a, "b":{"a":self.b.a}}
The model looks something like:
class Foo(models.Model):
A_TYPES = (
("bar", "Bar"),
("baz", "Baz")
)
a = models.CharFields(max_length20, choices=A_TYPES)
b = models.ManyToManyField("self")
c = models.IntegerField(null=True)
d = models.BooleanField(default=False)
TestCase
are you extending? Presumably it is Django's version and notunittest.TestCase
? – Motheaten