Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jgram925/0e9d4ccbdcbb56d4c8bd6a9b09a06ec2 to your computer and use it in GitHub Desktop.
Save jgram925/0e9d4ccbdcbb56d4c8bd6a9b09a06ec2 to your computer and use it in GitHub Desktop.
Django Unit Testing with unittest & coverage.md

Source: credited to Unittest

Source: credited to Django

Source: credited to Agiliq

Source: credited to Toast Driven

Source: credited to Stack Overflow answer

Collecting modules to test with Coverage

Install coverage using pip.

pip install coverage

Collect modules for testing.

coverage run --source=app1,app2 --omit=*/migrations/*  manage.py test

Run coverage report to plan modules for testing.

coverage report -m

Test creation best practices

Keep tests simple and well organized. Instead of using the test.py module, create a directory and create a test for each module you'll be testing for. Example below...

app_name
    tests
        __init__.py
        test_forms.py
        test_models.py
        test_views.py

Imports on the modules must include the app (example below)

from...

from .models import *

to...

from app.models import *

Common Unittest Methods

assertEqual(a, b) = a == b

assertNotEqual(a, b) = a != b

assertTrue(x) = bool(x) is True

assertFalse(x) = bool(x) is False

assertIs(a, b) = a is b

assertIsNot(a, b) = a is not b

assertIsNone(x) = x is None

assertIsNotNone(x) = x is not None

assertIn(a, b) = a in b

assertNotIn(a, b) = a not in b

assertIsInstance(a, b) = isinstance(a, b)

assertNotIsInstance(a, b) = not isinstance(a, b)

Example test on model

from django.test import TestCase, Client
from calib_inv.models import Gauge, History
from calib_inv.admin import HistoryAdmin
from calib_inv.views import *
import datetime

class GaugeTest(TestCase):
    def setUp(self):
        self.gauge = Gauge.objects.create(
            serial_number = "123456",
            manufacturer = "Test Co.",
            description = "Test description text...",
            location = "Test, Location",            
            frequency_of_use = "Infrequently",
            calibration_method = "inhouse",
            calibration_source = "ENGINEER",
            calibration_interval = Gauge.two_years,
        )

    def test_new_save(self):
        interval = self.gauge.calibration_interval
        created = self.gauge.created_date.replace(microsecond=0)
        last_mod = self.gauge.last_modified.replace(microsecond=0)
        self.assertEqual(str(self.gauge.serial_number), "123456")
        self.assertEqual(self.gauge.calibration_due_date, 
            self.gauge.last_calibration 
                + datetime.timedelta(days=interval))
        self.assertEqual(created, last_mod)
        self.assertFalse(self.gauge.out_for_calibration)
        self.assertFalse(self.gauge.archived)

    def test_exist_save(self):
        gauge = Gauge.objects.get(serial_number='123456')
        gauge.save()
        interval = gauge.calibration_interval
        self.assertEqual(gauge.calibration_due_date, 
            gauge.last_calibration 
                + datetime.timedelta(days=interval))

Example on view with force_login & multiple test functions

class PageViewsTest(TestCase):
    def setUp(self):
        self.client = Client()
        self.client.force_login(User.objects.get_or_create(
            username='testuser')[0])

    def matchingAsserts(self, response, page_param):
       self.assertEqual(response.status_code, 200)
       self.assertIsNotNone(response.context['json_src'])
       self.assertTemplateUsed(response, 'base.html')
       self.assertTemplateUsed(response, 'lists.html')
       self.assertTemplateUsed(response, 'js/gauge_records.html')
       self.assertEqual(response.context['page_title'], page_param)

    def test_index(self):        
       response = self.client.get('/calib_inv/')
       page_arg = 'All Gauge Calibration Records'
       self.matchingAsserts(response, page_arg)

    def test_pastdue(self):        
       response = self.client.get('/calib_inv/past_due/')
       page_arg = 'Past Due Gauge Calibration Records'
       self.matchingAsserts(response, page_arg)

Example Posting & Redirects

def test_post_edit_history(self):        
    post_resp = self.client.post(
        '/calib_inv/123456/history/', 
            data={            
                'date': datetime.date.today(),
                'results': 'tested good!',
                'initials': 'JR',
            }
        )
    self.assertRedirects(
            post_resp, 
            '/calib_inv/123456/', 
            status_code=302, 
            target_status_code=200
        )

Example Admin Function

def test_admin(self):
    obj = History.objects.get(pk=1)
    ha_fun = HistoryAdmin.gauge_sn(History, obj)
    return_statement = obj.gauge.serial_number
    self.assertEqual(ha_fun, return_statement)

Checking tests code coverage

Collect module tests to find coverage.

coverage run --source=app1,app2 --omit=*/migrations/*  manage.py test

Run coverage report to see coverage.

coverage report -m

Extra Notes

Testing Exceptions and 404s

From Django Documentation

If you point the test client at a view that raises an exception, that exception will be visible in the test case. You can then use a standard try ... except block or assertRaises() to test for exceptions.

The only exceptions that are not visible to the test client are Http404, PermissionDenied, SystemExit, and SuspiciousOperation. Django catches these exceptions internally and converts them into the appropriate HTTP response codes. In these cases, you can check response.status_code in your test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment