In modern days, automated software testing is extremely important to kill the bugs during development. Unit testing is the first level of software testing, which a developer can not and should not avoid. Because it helps the developer to write bug-free, secure and robust codes.

What is Unit Testing?

Unit testing is a software testing method where every individual component and every single unit of the software is tested. Most of the time, unit testing is performed by the developer. Some times it is done by a QA engineer.

When you are doing TDD (Test Driven Development), you will write the unit test before starting the actual code. You can also write unit tests after finishing the specific feature. However, unit testing is performed during the coding stage and before the integration test.

Why Unit Testing?

Writing tests take time. Some times, because of a tight deadline, we may cut out the testing part from the development. But in the long run, it may create bigger problems that take more time and money. So, it is highly recommended that you always write unit tests at the development stage.

Here we are covering some benefits of writing unit tests.

  • Reduce the Burden: As a programmer, we always worry about the feature we develope, the commit we push, whether it will work as expected or not. As a result, it affects our peace of mind and even our personal lives. So, writing unit tests will help you to reduce the burden and eliminate the worry. Additionally, it will boost your confidence level in the code.
  • Quality Code: If you want to write high-quality code, you must adopt writing tests. The more you write tests, the better. You should always try to reach 100% test coverage to maintain the quality of code.
  • Hunting Bug: Unit testing forces you to think about all the edge cases and unexpected scenarios before going to production. As a result, you will hunt all the bugs and achieve bug-free status ahead of time. Even more, If you make changes in the future, you will be able to find which part of your application is more like to break. So, with unit testing, you will get the opportunity to keep the project bug-free from time to time.
  • Self Documentation: Code speaks louder than words. So, unit tests will work as a documentation of your system. Therefore, it will help other developers to understand the functionality only by looking at the unit tests.
  • Reduce Time and Costs: Detecting a bug is a challenging task. The sooner you detect a bug, the better. So, if you detect a bug in the development phage, it will save a lot of time in the long run, which ultimately reduces the development costs.

Testing Tools

In the previous article, we built a GraphQL API with Django-Graphene for a simple blog project. Here we will test those APIs with Pytest. To generate mock data we will use the Mixer. Though Django-Graphene has a utility class called GraphQLTestCase, we will use the default TestCase from django.test module. As we are using Pytest, we can go without it and write the functional tests. But here, I would prefer to share the class-based test cases with default TestCase.

For the testing client, we will use Graphane Client class from graphene.test module. We can go without it by directly executing the schema with schema.execute() method.

1. Basic Setup

At first, we have to activate the virtual environment. Run the following command to activate your virtual environment.

source ./morning_env/bin/activate

Then install pytest-django with pip.

pip install pytest-django

And create a file in the root directory of your project called pytest.ini and add the following code.

[pytest]
DJANGO_SETTINGS_MODULE = morning_blog.settings

In your blog app, create a directory called tests. We will test here only blog APIs. So, create a file called test_blogs.py. Your folder structure should look like this:

Now add the following codes in test_blogs.py to import necessary modules.

import pytest
from django.test import TestCase
from mixer.backend.django import mixer
from graphene.test import Client

from blog.models import Author, Blog
from blog.schema.blogs import schema

Let’s create some variables for queries and mutations.

blog_list_query = """
    query {
        blogs {
            id
            title
            author {
                id
                name
            }
        }
    }
"""

single_blog_query = """
    query($id:ID!)
    {
        blog(id:$id) {
            id
            title
            author {
                id
                name
            }
        }
    }
"""

create_blog_mutation = """
     mutation CreateBlog($input: BlogInputType!) {
        createBlog(input: $input) {
            blog {
                id
                title
                author {
                    id
                    name
                }
            }
            ok
        }
    }
"""

update_blog_mutation = """
     mutation UpdateBlog($input: BlogInputType!) {
        updateBlog(input: $input) {
            blog {
                id
                title
            }
            ok
        }
    }
"""

delete_blog_mutation = """
    mutation DeleteBlog($input: DeleteBlogInputType!) {
        deleteBlog(input: $input) {
            ok
        }
    }
"""

2. Writing Unit Tests for GraphQL API

It’s time to write tests. I will write five test cases here to test the CRUD functionality of blog API.

@pytest.mark.django_db
class TestBlogSchema(TestCase):

    def setUp(self):
        self.client = Client(schema)
        self.blog = mixer.blend(Blog)

    def test_single_blog_query(self):
        response = self.client.execute(single_blog_query, variables={"id": self.blog.id})
        response_blog = response.get("data").get("blog")
        assert response_blog["id"] == str(self.blog.id)


    def test_blog_list_query(self):
        mixer.blend(Blog)
        mixer.blend(Blog)

        response = self.client.execute(blog_list_query)
        blogs = response.get("data").get("blogs")
        ok = response.get("data").get("ok")
        
        assert len(blogs)

    def test_create_blog(self):
        author = mixer.blend(Author)
        payload = {
            "title": "How to test GraphQL with pytest",
            "authorId": author.id,
            "body": "This is the example of functional testing.",
        }

        response = self.client.execute(create_blog_mutation, variables={"input": payload})
        blog = response.get("data").get("createBlog").get("blog")
        title = blog.get("title")
        assert title == payload["title"]

    def test_update_blog(self):
        payload = {
            "id": self.blog.id,
            "title": "How to test GraphQL update mutation with pytest"
        }

        response = self.client.execute(update_blog_mutation, variables={"input": payload})

        response_blog = response.get("data").get("updateBlog").get("blog")
        title = response_blog.get("title")
        assert title == payload["title"]
        assert title != self.blog.title 

    def test_delete_blog(self):
        payload = {
            "id": self.blog.id
        }
        response = self.client.execute(delete_blog_mutation, variables={"input": payload})
        ok = response.get("data").get("deleteBlog").get("ok")
        assert ok

3. Run the Test

Now go to your shell and run the following command to run the test.

py.test -s

You should see the following results in your terminal.

Conclusion

Unit testing is an automated software testing method where every small piece of the application is tested. It helps developers to hunt bugs early, write quality code, reduce development time and costs.

Here we wrote unit tests for a Django Graphane API with Pytest and covered five test cases to check out GraphQL CRUD functionality.

If you would like to see the full source code, you can check it out from here.