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.eapp1/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