Home > Blockchain >  How to test methods in models.py related to filters in django using pytest?
How to test methods in models.py related to filters in django using pytest?

Time:12-21

I have models which contains lots of classmethods for filtering different kinds of data. Now the problem is, these classmethods are called in views for several functionality. For example, I have Order table as shown below:

class Order(models.Model):
    user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    total = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    order_date = models.DateTimeField(auto_now_add=True)
    cancelled = models.BooleanField(default=False)
    items = models.ManyToManyField(Items, through='OrderItems')
    status = models.CharField(max_length=30, choices=choice_status, default='waiting')
    restaurant = models.ForeignKey(Restaurant, on_delete=models.SET_NULL, related_name='orders', null=True)
    razorpay_payment_id = models.CharField(max_length=100, null=True, blank=True)
    razorpay_order_id = models.CharField(max_length=100, null=True, blank=True)
    mode = models.CharField(max_length=10, choices=choice_mode, default='COD', null=True, blank=True)
    paid = models.BooleanField(default=False, null=True, blank=True)
    delivery_charges = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    user_address = models.TextField(max_length=500)
    user_address_lat = models.DecimalField(max_digits=9, decimal_places=6)
    user_address_long = models.DecimalField(max_digits=9, decimal_places=6)
    restaurant_address = models.TextField(max_length=500)
    restaurant_address_lat = models.DecimalField(max_digits=9, decimal_places=6)
    restaurant_address_long = models.DecimalField(max_digits=9, decimal_places=6)

    @classmethod
    def get_order_with_search_params(cls, params, queryset):
        search_order_id = None
        with contextlib.suppress(ValueError):
            search_order_id = int(params)

        return queryset.filter(
            Q(user__username__icontains=params) |
            Q(restaurant__name__icontains=params) |
            Q(id=search_order_id)
        )

    @classmethod
    def get_order_from_status(cls, params, queryset):
        return queryset.filter(Q(status=params.lower()))

    @classmethod
    def get_order_from_restaurant_id(cls, params, queryset):
        restaurant_id = None
        with contextlib.suppress(ValueError):
            restaurant_id = int(params)
        return queryset.filter(Q(restaurant_id=restaurant_id))

    @classmethod
    def get_object_from_pk(cls, pk):
        return get_object_or_404(cls, pk=pk)

    @classmethod
    def get_total_of_all_orders(cls, queryset=None):
        if not queryset:
            queryset = cls.objects.all()
        return queryset.filter(status='delivered').aggregate(total_sum=Sum('total'))['total_sum']

Now to test these functionality, I have to first have to do following:

  1. register user (restaurant type)

  2. activate this user

  3. add Items to store with restaurant user

  4. register another user (customer type)

  5. activate this user too

  6. add item to cart

  7. place order

    After placing order, it will create Order object, then I can test functionalities like filter. So this is integration testing.

Also along this steps, many related tables are affected like while registering users Address table will have been created, while adding items its CartItem table is populated, and many other things. This is the reason why I cannot just use Order.objects.create(**kwargs) and then test classmethods of model. Should I skip testing these classmethods? or their is any other ways to test these methods?

For ref.: I have only created testcases for fields and str methods shown below

def test_str_method(self):
    obj = State.objects.create(name='TestState')
    assert str(obj) == obj.name
def test_document_data(self, get_agent_document_form_initial_data):
    agent = User.objects.create(
        **{'username': 'test_agent1', 'mobile_number': ' 917894541211', 'email': '[email protected]'})
    document_obj = Document.objects.create(agent=agent,
                                           **get_agent_document_form_initial_data(pancard_document=filename,
                                                                                  license_document=filename))

    assert document_obj.agent == agent
    # assert document_obj.id == 1
    assert document_obj.is_verified is False
    assert document_obj.account_no == 1234567890
    assert document_obj.ifsc_code == 'ABCD0123456'
    assert document_obj.pancard_number == 'BNZAA2318J'
    assert document_obj.ifsc_code == 'ABCD0123456'
    assert document_obj.license_number == 'HR-0619850034761'
    assert document_obj.pancard_document == './media/default.jpg'
    assert document_obj.license_document == './media/default.jpg'
    assert document_obj.razorpay_contact_id is None
    assert document_obj.razorpay_fund_account_id is None
    assert str(document_obj) == f'{document_obj.id} | {document_obj.agent} | {document_obj.is_verified}'

Just checking if values of fields are correct or not. But I want to write testcases for other methods in models which is related to flow of project.

CodePudding user response:

You can prepopulate data in Database, For better understanding, you can follow this link.

And then simply test your method shown below:

 class TestOrderModelMethods:
       def test_get_order_from_status(self):
                   assertQuerysetEqual(
                   Order.get_order_from_status(params='waiting', queryset=Order.objects.all()),
                   Order.objects.filter(status='waiting'),
                   ordered=False)
  • Related