单元测试就是要测试这些方法执行后的确返回了上面预期的结果,我们在 test_models.py 中新增一个类,叫做 PostModelTestCase,在这个类中编写上述单元测试的用例。
from django.apps import apps class PostModelTestCase(TestCase): def setUp(self): # 断开 haystack 的 signal,测试生成的文章无需生成索引 apps.get_app_config('haystack').signal_processor.teardown() user = User.objects.create_superuser( username='admin', email='admin@hellogithub.com', password='admin') cate = Category.objects.create(name='测试') self.post = Post.objects.create( title='测试标题', body='测试内容', category=cate, author=user, ) def test_str_representation(self): self.assertEqual(self.post.__str__(), self.post.title) def test_auto_populate_modified_time(self): self.assertIsNotNone(self.post.modified_time) old_post_modified_time = self.post.modified_time self.post.body = '新的测试内容' self.post.save() self.post.refresh_from_db() self.assertTrue(self.post.modified_time > old_post_modified_time) def test_auto_populate_excerpt(self): self.assertIsNotNone(self.post.excerpt) self.assertTrue(0 < len(self.post.excerpt) <= 54) def test_get_absolute_url(self): expected_url = reverse('blog:detail', kwargs={'pk': self.post.pk}) self.assertEqual(self.post.get_absolute_url(), expected_url) def test_increase_views(self): self.post.increase_views() self.post.refresh_from_db() self.assertEqual(self.post.views, 1) self.post.increase_views() self.post.refresh_from_db() self.assertEqual(self.post.views, 2)这里代码虽然比较多,但做的事情很明确。setUp 方法会在每一个测试案例运行前执行,这里做的事情是在数据库中创建一篇文章,用于测试。
接下来的各个 test_* 方法就是对于各个功能单元的测试,以 test_auto_populate_modified_time 为例,这里我们要测试文章保存到数据库后,modifited_time 被正确设置了值(期待的值应该是文章保存时的时间)。
self.assertIsNotNone(self.post.modified_time) 断言文章的 modified_time 不为空,说明的确设置了值。TestCase 类提供了系列 assert* 方法用于断言测试单元的逻辑结果是否和预期相符,一般从方法的命名中就可以读出其功能,比如这里 assertIsNotNone 就是断言被测试的变量值不为 None。
接着我们尝试通过
self.post.body = '新的测试内容' self.post.save()修改文章内容,并重新保存数据库。预期的结果应该是,文章保存后,modifited_time 的值也被更新为修改文章时的时间,接下来的代码就是对这个预期结果的断言:
self.post.refresh_from_db() self.assertTrue(self.post.modified_time > old_post_modified_time)这个 refresh_from_db 方法将刷新对象 self.post 的值为数据库中的最新值,然后我们断言数据库中 modified_time 记录的最新时间比原来的时间晚,如果断言通过,说明我们更新文章后,modified_time 的值也进行了相应更新来记录修改时间,结果符合预期,测试通过。
其它的测试方法都是做着类似的事情,这里不再一一讲解,请自行看代码分析。
测试视图视图函数测试的基本思路是,向某个视图对应的 URL 发起请求,视图函数被调用并返回预期的响应,包括正确的 HTTP 响应码和 HTML 内容。
我们的博客应用包括以下类型的视图需要进行测试:
首页视图 IndexView,访问它将返回全部文章列表。
标签视图,访问它将返回某个标签下的文章列表。如果访问的标签不存在,返回 404 响应。
分类视图,访问它将返回某个分类下的文章列表。如果访问的分类不存在,返回 404 响应。
归档视图,访问它将返回某个月份下的全部文章列表。
详情视图,访问它将返回某篇文章的详情,如果访问的文章不存在,返回 404。
自定义的 admin,添加文章后自动填充 author 字段的值。
RSS,返回全部文章的 RSS 内容。
首页视图、标签视图、分类视图、归档视图都是同一类型的视图,他们预期的行为应该是:
返回正确的响应码,成功返回200,不存在则返回404。
没有文章时正确地提示暂无文章。
渲染了正确的 html 模板。
包含关键的模板变量,例如文章列表,分页变量等。
我们首先来测试这几个视图。为了给测试用例生成合适的数据,我们首先定义一个基类,预先定义好博客的数据内容,其它视图函数测试用例继承这个基类,就不需要每次测试时都创建数据了。我们创建的测试数据如下:
分类一、分类二
标签一、标签二
文章一,属于分类一和标签一,文章二,属于分类二,没有标签
class BlogDataTestCase(TestCase): def setUp(self): apps.get_app_config('haystack').signal_processor.teardown() # User self.user = User.objects.create_superuser( username='admin', email='admin@hellogithub.com', password='admin' ) # 分类 self.cate1 = Category.objects.create(name='测试分类一') self.cate2 = Category.objects.create(name='测试分类二') # 标签 self.tag1 = Tag.objects.create(name='测试标签一') self.tag2 = Tag.objects.create(name='测试标签二') # 文章 self.post1 = Post.objects.create( title='测试标题一', body='测试内容一', category=self.cate1, author=self.user, ) self.post1.tags.add(self.tag1) self.post1.save() self.post2 = Post.objects.create( title='测试标题二', body='测试内容二', category=self.cate2, author=self.user, created_time=timezone.now() - timedelta(days=100) )