Heitor Chang
Heitor Chang

Reputation: 6057

Creating related objects in a TestCase is not seen in Model method

I am in the middle of writing tests and must be missing something basic, because it appears that objects that I create remain isolated.

I have Customers that can hold a number of Items. Inside the test, after creating Items (with a price) belonging to a Customer, I want to sum the prices of the items for a Customer (using a Customer method), but keep getting 0.

The actual code is more complex, so I simplified the details while keeping the structure and logic the same.

The version with the "production" database works fine, so there must be something about the way tests and their databases are run that I am missing.

### models.py

class Customer(models.Model):
    name = models.CharField(max_length=20)

    def compute_total(self):
        total = 0
        items = self.item_set.all()

        for item in items:
            total += item.price
        return total

    def __str__(self):
        return self.name

class Item(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    name = models.CharField(max_length=20)
    price = models.IntegerField()

    def __str__(self):
        return "%s %s (%s)" % (self.customer, self.name, self.price)

### tests.py

class CustomerTests(TestCase):
    def test_add_two_items(self):
        ken = Customer(name="Ken")
        apple = Item(customer=ken, name="apple", price=10)
        banana = Item(customer=ken, name="banana", price=2)

        print(apple)  # make sure apple is created, and it is.
        self.assertEquals(ken.compute_total(), 12)

For the "production" version, I manually add Joe and two fruits as above in the admin, and test it with:

### views.py

def index(request):
    joe = Customer.objects.get(name="Joe")

    return HttpResponse(joe.compute_total())

Here, the total is what I expect, so how do I achieve this behavior inside a test?

Upvotes: 0

Views: 109

Answers (1)

Tomasz Nocoń
Tomasz Nocoń

Reputation: 418

What happens is when you use item_set in compute_total function it queries the database for all items with customer_id of ken. But there are no items, and there is no ken because you didn't actually persisted anything into the database. You just created some objects.

So what you need to do is:

ken = Customer(name="Ken")
ken.save() # Ken must be persisted before creating his items.
apple = Item(customer=ken, name="apple", price=10)
banana = Item(customer=ken, name="banana", price=2)
apple.save()
banana.save()

First we need to persist ken, so that the database creates an ID for him. Then we create apple and banana with that id as a foreign key, and persist them as well.

Upvotes: 1

Related Questions