Django unit testing using factoryboy

Django unit testing using factoryboy

what is a factory boy?

factory boy is a fixtures replacement tool, it aims to replace static, hard to maintain fixtures with easy-to-use factories for complex objects.

how to use factory boy with django?

factoryboy provides a class DjangoModelFactory as a base class for django unit test factories.

  • install the factory boy with pip
    pip install factory_boy
    
  • follow the below directory structure for django apps
    ├── app1
    │   ├── __init__.py
    │   ├── models.py
    │   ├── urls.py
    │   └── views.py
    └── tests
        ├── factories
        │   ├── __init__.py
        │   └── question.py
        ├── views
        │   ├── __init__.py
        │   └── test_question_list.py
        └── __init__.py
    
  • The factories folder contains the factory modules that will require to create related model objects in the testing phase.
  • Let's see a model code for model Question (i.e app1/models.py)
from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
  • Let's see the code for factory code for question (i.e tests/factories/question.py)
import factory

class QuestionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = 'app1.Question'

    question_text = factory.Faker('name')
    pub_date = factory.Faker('date', end_datetime=datetime.date.today())
  • Let's see code for list questions view (i.e app1/views.py)
from .models import Question
from rest_framework.response import Response

def list_questions(request):
    questions = Question.objects.all().values("id", "question_text")
    data = {"questions": questions}
    return Response(data,content_type="application/json")
  • Now, let's use factoryboy and write the test case for questions list api. The url path for the API is api/list-questions/
from django.test import TestCase, Client

from .tests.factories.question import QuestionFactory

class TestQuestionsListAPI(TestCase):
    def setUp(self):
        self.q1 = QuestionFactory(question_text="who created python?")
        self.q2 = QuestionFactory(question_text="who created golang?")
        client = Client()

    def test_questions_list(self):
        url = 'api/list-questions/'
        resp = self.client.get(url)
        questions = resp.json().get("questions")
        question_titles = [for q.get("question_text") for q in questions]
        self.assertEqual(resp.status_code, 200)
        self.assertTrue(self.q1.question_text in question_titles)
        self.assertTrue(self.q2.question_text in question_titles)
  • In the above example, we have created two objects with factory and passed the question_text value. We can have any number of objects as we want based on our requirement.

  • We can do more things with factory boy

    • Sequences: When a field has a unique key, each object generated by the factory should have a different value for that field.
    • LazyFunction: In simple cases, calling a function is enough to compute the value. If that function doesn’t depend on the object being built, use LazyFunction to call that function;
    • LazyAttribute: Some fields may be deduced from others, for instance the email based on the username. The LazyAttribute handles such cases.

For more information read the official documentation at https://factoryboy.readthedocs.io/en/stable/index.html