Home > Blockchain >  Problem with testing custom admin actions
Problem with testing custom admin actions

Time:11-15

Running into some problems when testing my custom admin actions.

First I can show you an example of a test that works and the actions it's testing.

custom action, Product model

   @admin.action(description="Merge selected products")
   def merge_products(self, request, queryset):

    list_of_products = queryset.order_by("created_at")
    list_of_products = list(list_of_products)
    canonical_product = list_of_products[0]
    list_of_products.remove(canonical_product)
    for p in list_of_products:
        for ep in p.external_products.all():
            ep.internal_product = canonical_product
            ep.save()
        p.save()
    canonical_product.save()

related test

class MockRequest(object):
    pass


class ProductAdminTest(TestCase):
    def setUp(self):
        self.product_admin = ProductAdmin(model=Product, admin_site=AdminSite())

        User = get_user_model()
        admin_user = User.objects.create_superuser(
            username="superadmin", email="[email protected]", password="testpass123"
        )
        self.client.force_login(admin_user)

    def test_product_merge(self):
        self.boot1 = baker.make("products.Product", title="Filippa K boots", id=uuid4())
        self.boot2 = baker.make("products.Product", title="Filippa K boots", id=uuid4())
        self.external_product1 = baker.make(
            "external_products.ExternalProduct", internal_product=self.boot1
        )
        self.external_product2 = baker.make(
            "external_products.ExternalProduct", internal_product=self.boot2
        )

        self.assertEqual(self.boot1.external_products.count(), 1)
        self.assertEqual(self.boot2.external_products.count(), 1)

        request = MockRequest()
        queryset = Product.objects.filter(title="Filippa K boots")
        self.product_admin.merge_products(request, queryset)

        self.assertEqual(self.boot1.external_products.count(), 2)
        self.assertEqual(self.boot2.external_products.count(), 0)

Might not be the pretties test but it works, so does the action.

The code above works as it should but has been running into problems when trying to test an almost identical action but for another model.

custom action, Brand model

    @admin.action(description="Merge selected brands")
    def merge_brands(self, request, queryset):
        qs_of_brands = queryset.order_by("created_at")
        list_of_brands = list(qs_of_brands)
        canonical_brand = list_of_brands[0]
        for brand in list_of_brands:
            if brand.canonical:
                canonical_brand = brand
        list_of_brands.remove(canonical_brand)

        for brand in list_of_brands:

            brand.canonical_brand = canonical_brand
            brand.save()

            for product in Product.objects.filter(brand=brand):
                product.brand = canonical_brand
                product.save()

        canonical_brand.save()

related test

class MockRequest(object):
    pass


    def setUp(self):
        self.brand_admin = BrandAdmin(model=Brand, admin_site=AdminSite())

        User = get_user_model()
        admin_user = User.objects.create_superuser(
            username="superadmin", email="[email protected]", password="testpass123"
        )
        self.client.force_login(admin_user)


    def test_brand_merge(self):
        self.brand1 = baker.make("brands.Brand", title="Vans")
        self.brand1.canonical_brand = self.brand1
        self.brand1.active = True
        self.brand1.save()

        self.brand2 = baker.make("brands.Brand", title="Colmar")

        self.boot1 = baker.make("products.Product", brand=self.brand1, id=uuid4())
        self.boot2 = baker.make("products.Product", brand=self.brand2, id=uuid4())

        self.assertEqual(self.boot1.brand, self.brand1)
        self.assertEqual(self.boot2.brand, self.brand2)
        self.assertEqual(self.brand1.active, True)
        self.assertEqual(self.brand2.active, False)

        request = MockRequest()
        queryset = Brand.objects.all()

        self.brand_admin.merge_brands(request, queryset)

        self.assertEqual(self.boot1.brand, self.brand1)
        self.assertEqual(self.boot2.brand, self.brand1)
        self.assertEqual(self.brand1.active, True)
        self.assertEqual(self.brand2.active, False)
        self.assertEqual(self.brand1.canonical_brand, self.brand1)
        self.assertEqual(self.brand2.canonical_brand, self.brand1)


It's not really necessary how the code above works but I included it for context.

The product admin action works both when I try it manually and in the test suite. The brand action does work as it should when tested manually but in the test suite, it does nothing.

self.brand_admin.merge_brands(request, queryset)

does not change the tested objects in any way.

I have another custom admin action for the Brand model and it's the same problem there, works like a charm when I try it manually but it does nothing in the test suite. Because of those facts, my guess is that the problem is related to my admin setup in the test suite but it's identical to the admin setup used for the first test which works.

Any ideas?

Gladly provide more context if needed.

CodePudding user response:

It looks like you need to refresh the objects from the Database, in your test self.brand1, boot1, etc are in memory and will not be automatically updated from the database, you need to get the new values from the database.

https://docs.djangoproject.com/en/4.0/ref/models/instances/#refreshing-objects-from-database

If you need to reload a model’s values from the database, you can use the refresh_from_db() method. When this method is called without arguments the following is done:

All non-deferred fields of the model are updated to the values currently present in the database. Any cached relations are cleared from the reloaded instance.

After you call your merge_brands you should refresh each object.

self.boot1.refresh_from_db()
self.boot2.refresh_from_db()
# etc..       
  • Related