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.
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.
Python1from 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.
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:
Python1class 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
andself.y
are numeric variables.self.list1
,self.list2
, andself.list3
are lists.
These variables will be used in our subsequent 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_
.
Python1class 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 if10 + 5
is equal to15
.test_subtraction
checks if10 - 5
is equal to5
.test_multiplication
checks if10 * 5
is equal to50
.test_division
checks if10 / 5
is equal to2
.
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.
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:
Python1class 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 isTrue
.assertFalse
checks if a condition isFalse
.assertListEqual
checks if two lists are equal.
To demonstrate the creation of multiple test cases, let's create a separate dictionary-related tests with a new test case class:
Python1class 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.
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:
Bash1python 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.
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:
Python1class 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.
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!