Lesson 1
Introduction to Django Testing
Introduction to Django Testing

Welcome to the first lesson of our course on building a TODO App with Django. In this course, we will cover how to implement APIs and test them using Django REST Framework. Testing is a crucial part of software development because it ensures that our code works as expected and helps catch bugs early.

In this lesson, we will focus on understanding the basics of Django testing. By the end of this lesson, you will know how to set up and write basic tests using Django's TestCase framework.

Understanding the TestCase Class

The core of Django testing revolves around the TestCase class, which is part of the django.test module. This class allows you to create and run tests using a variety of assertion methods to check the correctness of your code.

Python
1from django.test import TestCase

Think of the TestCase class as a blueprint for creating individual tests. It provides various methods to simulate different test scenarios and check for expected outcomes.

Setting Up the Test Cases

In this lesson, we won't yet write actual tests for the Todo project. Instead, we will recall and practice the very basics of Python's testing using simple operations with integers, lists and dictionaries. In the next lesson, we will start building actual tests.

Before writing our test cases, we need to set up the necessary data. This is where the setUp method comes into play. The setUp method is used to initialize any data or state that will be used across multiple test methods.

Here is an example setup:

Python
1class SampleTestCase(TestCase): 2 def setUp(self): 3 self.x = 10 4 self.y = 5 5 self.list1 = [1, 2, 3] 6 self.list2 = [1, 2, 3] 7 self.list3 = [1, 2, 3, 4]

In this setup:

  • self.x and self.y are numeric variables.
  • self.list1, self.list2, and self.list3 are lists.

These variables will be used in our subsequent test methods.

Writing Basic Test Methods

Let's write some basic test methods to understand how the TestCase class works. We'll test simple arithmetic operations like addition, subtraction, multiplication, and division. Each test within a test case is represented as a class method, and its name must start with test_.

Python
1class SampleTestCase(TestCase): 2 def test_addition(self): 3 self.assertEqual(self.x + self.y, 15) 4 5 def test_subtraction(self): 6 self.assertEqual(self.x - self.y, 5) 7 8 def test_multiplication(self): 9 self.assertEqual(self.x * self.y, 50) 10 11 def test_division(self): 12 self.assertEqual(self.x / self.y, 2)

Here:

  • test_addition checks if 10 + 5 is equal to 15.
  • test_subtraction checks if 10 - 5 is equal to 5.
  • test_multiplication checks if 10 * 5 is equal to 50.
  • test_division checks if 10 / 5 is equal to 2.

Each test method uses self.assertEqual to compare the expected result with the actual result. An assertion is a statement in programming used to determine if a condition is true. If the condition evaluates to true, the program continues to run. If it evaluates to false, the program throws an error. In testing, assertions are used to validate that the output of a function or a piece of code matches the expected result.

Using Assertions in Django Tests

Assertions are the backbone of your tests. They allow you to verify whether your code behaves as expected. We have already seen assertEqual, but there are various other assertion methods that you can use.

Let's see some more examples:

Python
1class SampleTestCase(TestCase): 2 def test_assert_in(self): 3 self.assertIn(1, self.list1) 4 self.assertNotIn(4, self.list1) 5 6 def test_assert_true_false(self): 7 self.assertTrue(self.x > self.y) 8 self.assertFalse(self.y > self.x) 9 10 def test_assert_list_equal(self): 11 self.assertListEqual(self.list1, self.list2) 12 self.assertListEqual(self.list1, self.list3[:3])

In these examples:

  • assertIn checks if an element is in a list.
  • assertNotIn checks if an element is not in a list.
  • assertTrue checks if a condition is True.
  • assertFalse checks if a condition is False.
  • assertListEqual checks if two lists are equal.
Separate TestCase for Dictionary Assertions

To demonstrate the creation of multiple test cases, let's create a separate dictionary-related tests with a new test case class:

Python
1class DictionaryTestCase(TestCase): 2 def setUp(self): 3 self.dic1 = {"key1": "value1", "key2": "value2"} 4 self.dic2 = {"key1": "value1", "key2": "value2"} 5 self.dic3 = {"key1": "value1", "key3": "value3"} 6 7 def test_assert_dict_equal(self): 8 self.assertDictEqual(self.dic1, self.dic2) 9 self.assertDictEqual(self.dic1, {"key1": "value1", "key2": "value2"}) 10

In this test case:

  • assertDictEqual checks if two dictionaries are equal.
Running the Tests

To run your tests, use Django's manage.py script. Navigate to your project's directory in the command line and execute the following command:

Bash
1python manage.py test

Important Note: By default, Django writes test results to STDERR, so you will see the test results in the error tab, even if the tests ran without errors.

Sample output might look like this:

1Creating test database for alias 'default'... 2System check identified no issues (0 silenced). 3.. 4---------------------------------------------------------------------- 5Ran 2 tests in 0.001s 6 7OK

Interpretation of the output:

  • Creating test database...: A temporary database is created for testing.
  • System check identified no issues: Django ran its system checks and found no issues.
  • ..: Each dot represents a passed test.
  • Ran 2 tests: The number of tests that were run.
  • OK: All tests passed successfully.
What Happens When a Test Fails

When a test fails, Django's test runner will output a detailed message indicating what went wrong. For example, let's modify our test_division method to make it fail:

Python
1class SampleTestCase(TestCase): 2 def test_division(self): 3 self.assertEqual(self.x / self.y, 3) # This is intentionally incorrect

Running this test will yield an output similar to:

1Creating test database for alias 'default'... 2System check identified no issues (0 silenced). 3F 4====================================================================== 5FAIL: test_division (__main__.SampleTestCase) 6---------------------------------------------------------------------- 7Traceback (most recent call last): 8 File "test_sample.py", line 15, in test_division 9 self.assertEqual(self.x / self.y, 3) 10AssertionError: 2.0 != 3 11 12---------------------------------------------------------------------- 13Ran 1 test in 0.001s 14 15FAILED (failures=1)

Interpretation of the output:

  • F: Indicates a failed test.
  • FAIL: test_division (__main__.SampleTestCase): Specifies the test method that failed.
  • AssertionError: The error that caused the failure.
  • 2.0 != 3: Shows the expected result (3) versus the actual result (2.0).

This detailed message helps in quickly identifying and fixing the issue in the test or the code being tested.

Overview and Next Steps

In this lesson, we covered the basics of setting up and writing tests using Django's Test framework. You learned how to:

  • Set up a test case with the setUp method.
  • Write basic test methods for arithmetic operations.
  • Use various assertion methods to validate your code.

Next, you will have the opportunity to practice these concepts with hands-on exercises. In the subsequent lessons, we will focus on creating actual tests for our TODO application.

Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.