lukik
lukik

Reputation: 4060

How to avoid a trip to the database in this Test case

How do I override the django model with a Factory object so as to avoid hitting the database.

models.py

from django.db import models

class ApplicationType(models.Model):
    """
    Types of applications available in the system/
    """
    title = models.CharField(max_length=30)

    def __str__(self):
        return self.title

utils.py

from .models import ApplicationType

self.base_details = {}

def get_application_type(self, value):
 """
 Get types of applications. When successful it Populates the 
 self.base_details with an application_type key

 Args:
     value (object): value to be parsed

 Returns:
     bool:  True when value is ok, Else false

 Raises:
 """
 item_name = "Application Type"
 self.base_details['application_type'] = None
 try:
     if value:
         try:
             result = ApplicationType.objects.get(title=value)  # <== How do I avoid hitting this DB object?
             self.base_details['application_type'] = result.id
             return True
         except ApplicationType.DoesNotExist:
             self.error_msg = "Invalid Value: {}".format(item_name)
             return False
     else:
         self.error_msg = "Blank Value: {}".format(item_name)
         return False
 except:
     raise

So to test, I create an ApplicationType factory

tests.py

import factory
import pytest
application_types = ['Type 1', 'Type 2']

class ApplicationTypeFactory(factory.Factory):
    class Meta:
        model = ApplicationType

    title = "application_type_title"


@pytest.mark.django_db()
def test_get_application_type_populates_dict_when_value_provided_exists_in_database(self):
    """Populates base_details dict when value is found in database"""
    for entry in application_types:
        application_type = ApplicationTypeFactory.build(title=entry)
        assert self.base_info_values.get_application_type(entry) == True
        assert self.base_info_values.base_details["application_type"] is not None

As such, how would you go about writing a test that will avoid hitting the database in the ApplicationType.objects.get() query which is smack in the middle of code? Can I pass the "Model" as an parameter to the function and would that be a good design?

You are free to provide an alternative structure for the application/functions especially to allow for better testing in this kind of scenario.

Am running Python3.5, pytest-django and factory_boy

Upvotes: 0

Views: 672

Answers (1)

Enrique Saez
Enrique Saez

Reputation: 2700

You can patch the call to the database, to return a predefined value set by you. In your case, you could do something like this:

import factory
import pytest
from unittest.mock import Mock, patch
application_types = ['Type 1', 'Type 2']
@pytest.mark.django_db()
@patch('ApplicationType.objects.get')
def test_get_application_type_populates_dict_when_value_provided_exists_in_database(self, db_mocked_call):
    """Populates base_details dict when value is found in database"""
    mocked_db_object = {'id': 'test_id'}
    db_mocked_call.return_value = mocked_db_object
    for entry in application_types:
        application_type = ApplicationTypeFactory.build(title=entry)
        assert self.base_info_values.get_application_type(entry) == True
        assert self.base_info_values.base_details["application_type"] is not None

I suggest you to check as well pytest.parametrize to avoid making use of the for loop in your test, read more about it here: http://doc.pytest.org/en/latest/parametrize.html

In your example the test could look something like the following:

@pytest.mark.django_db()
@pytest.mark.parametrize("entry", ['Type 1', 'Type 2'])
@patch('ApplicationType.objects.get')
def test_get_application_type_populates_dict_when_value_provided_exists_in_database(self, db_mocked_call, entry):
    """Populates base_details dict when value is found in database"""
    mocked_db_object = {'id': 'test_id'}
    db_mocked_call.return_value = mocked_db_object
    application_type = ApplicationTypeFactory.build(title=entry)
    assert self.base_info_values.get_application_type(entry) == True
    assert self.base_info_values.base_details["application_type"] is not None

Upvotes: 1

Related Questions