Django: Testing The What, Why and How (Theory).

rabbilyasar

Rabbil Yasar Sajal

Posted on June 18, 2021

Django: Testing The What, Why and How (Theory).

A website cannot be perfect as it is nearly impossible for a website to run perfectly the first time without errors. As the project gets large it becomes hard for all the developers to keep track of all the changes; one single change in an existing component can make the whole project to break. This is why it is very essential to test every single functionality.

Table Of Content

  1. Types Of Testing
  2. What Should Be Tested
  3. Our Testing Project
  4. Structure
  5. Third Party Packages
  6. Django Package
  7. Testing
  8. Next

Types Of Testing

Unit and Integration testing are two main types of testing.

  • Unit Tests are isolated tests responsible for only one functionality.
  • Integration Tests are aimed at mimicking the user behavior, usually combined with multiple functions to make sure they behave correctly.

As Unit Tests are small they should be written all the time. These tests are easy to debug. The more you write these, the lesser you will likely to write integration testing. However, sometimes it is essential to run integration testing.

What Should Be Tested

  • If something can break, it should be tested. These includes models, views, forms, templates, validators and so on.
  • Each test should be focused on testing one functionality.
  • It should run whenever a code is being PUSHed or PULLed from a repo in the staging environment, before PUSHing to production.

That being said if the code in question is a builtin django or python function/library, don't test it. For example datetime or model fields. The only time you test something if the function or code is written by you.

Our Testing Project

In this tutorial we will be using a very basic project book-library.
To set up the project, first we will need to clone the repo.

git clone https://github.com/rabbilyasar/book-library.git
Enter fullscreen mode Exit fullscreen mode

Install virtualenv if not installed already

pip install virtualenv
Enter fullscreen mode Exit fullscreen mode

activate our virtualenv

source env/bin/activate
Enter fullscreen mode Exit fullscreen mode

install the packages in our requirements.txt

pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode
asgiref==3.3.4
autopep8==1.5.7
coverage==5.5
Django==3.2.4
mccabe==0.6.1
pycodestyle==2.7.0
pytz==2021.1
sqlparse==0.4.1
toml==0.10.2
Enter fullscreen mode Exit fullscreen mode

Migrate our database

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Structure

When you create a django app it includes a test.py file that you can use to write your tests, however, I prefer to create a module called tests and have separate files for our models, forms, views.
Note: If the project gets big it gets hard to maintain in this structure. Another way of doing would be to have a folder for models, views and forms and create file for each view or model.
We would be following the following structure for our project.

└── app_name
    └── tests
        ├── __init__.py
        ├── test_forms.py
        ├── test_models.py
        └── test_views.py

By default django's unittest module uses its builtin-test-discovery. It will discover tests under the current directory with filename pattern test*.py. For our current structure to work we will have to delete our existing tests.py file which has been provided by our django app.

Third Party Packages

  • coverage: It can be used to have a rough overview of a project's total test coverage. It is a remarkable tool for any beginner as it can give you suggestion on what needs to be tested.

  • mock: This is a utilitarian tool for isolating part of your code that are not critical to the test you are writing.

  • model-bakery: This is a fine tool for creating fixtures for testing. We will go through some usage and examples in the next blog.

Coverage

Let's a take a look at coverage and see how we can use it.
To run coverage inside a project:
Install coverage.

pip install coverage
Enter fullscreen mode Exit fullscreen mode

After every time you add some code to your application run this.

coverage run --omit='*/venv/*' manage.py test
Enter fullscreen mode Exit fullscreen mode

If you want to see more details you can add -v flag. In this case it is using verbosity level 2.

coverage run manage.py test -v 2
Enter fullscreen mode Exit fullscreen mode

After running it once we can can get a coverage report using:

coverage report
Enter fullscreen mode Exit fullscreen mode

We can also generate an HTML report in a new folder called htmlcov

coverage html
Enter fullscreen mode Exit fullscreen mode

Django Package

Django's default framework for testing is Python's standard library module unittest. Despite the name this module can be used for both unit test and integration test. There are other modules like pytest, which has it's own benefits and drawbacks on which we will focus later.

Client

Django's test client is an amazing tool which can be used for integration testing. It can simulate GET and POST requests on a URL and one can observe the response. Test Client.

Testing

TestCase

Django provides us with few base classes (SimpleTestCase, LiveServerTestCase, TransactionTestCase, TestCase). You can derive your test class from any of the base classes and have your own method which will test for a specific functionality.

class YourTestClass(TestCase):
    def setUp(self):
        # Setup run before every test method.
        pass

    def tearDown(self):
        # Clean up run after every test method.
        pass

    def test_something_that_will_pass(self):
        self.assertFalse(False)

    def test_something_that_will_fail(self):
        self.assertTrue(False)
Enter fullscreen mode Exit fullscreen mode

The best base class for most tests is django.test.TestCase. This creates a clean database before its tests are run, and runs every test function in its own transaction. The test client can be derived from this. TestCase provides us with an additional method setUpTestData along with setUp for setting up our data.

  • setUp(): It is called before every test method. This is useful when creating object that will need modifying before each test method.
  • setUpTestData(): This is called once before running the whole test. You use this one when you know the data you will create in this method will be the same for every test method. As this is run once for every test class, it is faster than setUp().

The tearDown() method is not very useful when it comes to test that involves database. Moreover TestCase does a full database flush at the start of a new test, so it will take care of the teardown for you.

We have some test methods provided to us by unittest, which can be used to test the condition of our code. Some standard assertions are:

  • AssertTrue: Checks if the condition is true.
  • AsserFalse: Checks if the condition is false.
  • AssertEqual: Checks if the condition is equal.

These are some of the python specific assert methods:
python unittest assert

Django specific assert methods:
django unittest assert

How To Run A Test

To run all the test in the project use the command:

python manage.py test
Enter fullscreen mode Exit fullscreen mode

This will discover all files with the pattern test*.py under the directory and run all tests defined.
If we want to show more test information we can change the verbosity like below:

python manage.py test --verbosity 2
Enter fullscreen mode Exit fullscreen mode

Note: Allowed verbosity levels are 0,1,2 and 3, the default being 1.
To run a specific test:

# Run the specified module
python3 manage.py test app.tests

# Run the specified module
python3 manage.py test app.tests.test_models

# Run the specified class
python3 manage.py test app.tests.test_models.YourTestClass

# Run the specified method
python3 manage.py test app.tests.test_models.YourTestClass.your_test_method
Enter fullscreen mode Exit fullscreen mode

Next

In the next part of our series we will get our hands dirty and will see how we can implement unit testing to our book-library.

If you like what I write and want to support me and my work, please follow me on twitter, GitHub, LinkedIn

💖 💪 🙅 🚩
rabbilyasar
Rabbil Yasar Sajal

Posted on June 18, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related