【图书介绍】《Django 5企业级Web应用开发实战(视频教学版)》_django 5企业级web应用开发实战(视频教学版)-CSDN博客
《Django 5企业级Web应用开发实战(视频教学版)》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com)
本节介绍关于Django框架自动化测试的内容,自动化测试是实际项目开发中必不可少的工具。
8.2.1 自动化测试概述
对于使用Django框架的Web开发人员而言,自动化测试是一个非常有用的解决Bug的工具。设计人员可以使用一组测试(一个测试套件)来解决或避免许多问题,具体包括:
(1)在编写新代码时,可以使用测试来验证代码是否按预期进行工作。
(2)重构或修改旧代码时,可以使用测试来确保所做的更改不会意外影响应用程序的行为。
测试Web应用程序是一项复杂的工作,因为Web应用程序通常是由几层逻辑组成的,从HTTP级别的请求处理到表单验证与处理,再到模板渲染等。借助Django的测试执行框架和各种实用程序,可以实现模拟HTTP请求、插入测试数据、检查应用程序的输出,以及验证常规代码是否在执行其应该做的事情。
8.2.2 编写和运行自动化测试
本小节主要分为两个部分,首先是说明如何使用Django框架编写测试,然后解释如何运行这些测试。学习完本小节的内容后,读者会发现Django框架的自动化测试确实很容易。
Django框架的单元测试使用了Python标准库模块unittest,该模块使用基于类的方法来定义测试。这里使用django.test.TestCase子类进行单元测试,django.test.TestCase是unittest.TestCase的子类,在事务中运行每个测试以提供独立的方式。
在编写实际测试用例之前,先简单介绍一下TestCase类的结构。常见的TestCase类由test_func()函数、setUp()函数和tearDown()函数组成。
- test_func()是指实际编写了测试逻辑的函数。
- setUp()函数是在test_func()函数之前执行的函数,常用于测试之前的初始化操作。
- tearDown()函数是在test_func()函数之后执行的函数,常用于测试结束之后的收尾操作。
首先,我们创建一个用于自动化测试的项目MyTestSite,并添加一个应用testapp。然后,创建一个用于测试的模型,具体代码如下:
【代码8-1】(详见源代码MyTestSite项目的testapp/models.py文件)
01 from django.db import models
02
03 # 在此处创建模型
04 class Students(models.Model):
05 name = models.CharField(max_length=32)
06 age = models.IntegerField()
【代码分析】
在第01行代码中,通过import关键字引入models模块。
在第04~06行代码中,创建了一个学生模型Students,继承自models.Model模型类。详细说明如下:
在第05行代码中,创建了一个CharField类型的name字段属性。
在第06行代码中,创建了一个IntegerField类型的age字段属性。
接下来,我们就可以在tests.py(创建应用时默认已存在)文件中编写自动化测试的代码了,具体如下:
【代码8-2】(详见源代码MyTestSite项目的testapp/tests.py文件)
01 from django.test import TestCase
02
03 from .models import Students
04
05 # Create your tests here.
06 class ModelTest(TestCase):
07 def setUp(self):
08 Students.objects.create(name='cici', age=7)
09 pass
10
11 def test_students_model(self):
12 s = Students.objects.get(name='cici')
13 self.assertEqual(s.name, 'cici')
14 pass
15
16 def tearDown(self):
17 pass
【代码分析】
在第01行代码中,通过import关键字引入TestCase模块。
在第03行代码中,通过import关键字引入测试代码中需要的Students模型。
在第06~17行代码中,创建了一个测试类ModelTest,继承自TestCase测试类,详细说明如下:
- 在第07~09行代码中,在setUp()函数中进行了Students模型的初始化操作。在第08行代码中,创建了一条Students模型的学生数据(name='cici', age=7),我们将使用这条数据进行测试 演示。
- 在第11~14行代码中,定义了一个测试方法test_students_model(),包含一个自身的self参数。注意,方法名称必须以“test”开头。其中,第13行代码通过self调用assertEqual()方法进行测试,判断学生的姓名是否与测试要求一致。
- 在第16、17行代码中,定义了tearDown()函数,这里没有定义实际的操作代码。
下面,我们通过命令行指令python manage.py test app进行自动化测试,效果如图8.1所示。
图8.1 MyTestSite项目自动化测试(1)
图8.1显示的是测试成功的情况,如果测试时发现错误会是什么情况呢?下面我们对【代码8-2】稍加修改,加入一些错误测试,具体代码如下:
【代码8-3】(详见源代码MyTestSite项目的testapp/tests.py文件)
01 from django.test import TestCase
02
03 from .models import Students
04
05 # Create your tests here.
06 class ModelTest(TestCase):
07 def setUp(self):
08 Students.objects.create(name='cici', age=7)
09 pass
10
11 def test_students_model(self):
12 s = Students.objects.get(name='cici')
13 self.assertEqual(s.name, 'cici')
14 self.assertEqual(s.age, 8)
15 pass
16
17 def tearDown(self):
18 pass
【代码分析】
在第14行代码中,通过self调用assertEqual()方法进行测试,判断学生的年龄是否与测试要求一致。
下面,我们再次通过命令行指令python manage.py test app进行自动化测试,效果如图8.2所示。初始化学生的年龄为7,当我们测试年龄值是否等于8时,给出了错误提示信息(很清楚提示了“7 != 8”)。
图8.2 MyTestSite项目自动化测试(2)
8.2.3 数据库自动化测试
本小节基于前一小节介绍的Django项目(MyTestSite)介绍数据库自动化测试的过程。
首先,完善一下用于测试的模型,具体代码如下:
【代码8-4】(详见源代码MyTestSite项目的testapp/models.pym文件)
01 from django.db import models
02
03 # 在此处创建模型
04 class Teachers(models.Model):
05 name = models.CharField(max_length=32)
06 pass
07
08 class Clazz(models.Model):
09 name = models.CharField(max_length=16)
10 teachers = models.ManyToManyField(Teachers)
11 pass
12
13 class Students(models.Model):
14 name = models.CharField(max_length=32)
15 age = models.IntegerField()
16 clazz = models.ForeignKey(Clazz, on_delete=models.CASCADE)
17 pass
【代码分析】
在第01行代码中,通过import关键字引入models模块。
在第04~06行代码中,创建了一个教师模型Teachers,继承自models.Model模型类,详细说明如下:
- 在第05行代码中,创建了一个CharField类型的name字段属性。
在第08~11行代码中,创建了一个班级模型Clazz,继承自models.Model模型类,详细说明如下:
- 在第09行代码中,创建了一个CharField类型的name字段属性。
- 在第10行代码中,创建了一个与教师模型Teachers多对多关系的字段属性teachers。
在第13~17行代码中,创建了一个学生模型Students,继承自models.Model模型类,详细说明如下:
- 在第14行代码中,创建了一个CharField类型的name字段属性。
- 在第15行代码中,创建了一个IntegerField类型的age字段属性。
- 在第16行代码中,创建了一个班级模型Clazz的外键字段属性clazz。
然后,我们就可以在tests.py(创建应用时默认已存在)文件中编写自动化测试的代码了,具体如下:
【代码8-5】(详见源代码MyTestSite项目的testapp/tests.py文件)
01 from django.test import TestCase
02
03 from .models import Teachers, Clazz, Students
04
05 # Create your tests here.
06 class ModelTest(TestCase):
07 def setUp(self):
08 t1 = Teachers.objects.create(name='liu')
09 t2 = Teachers.objects.create(name='guan')
10 t3 = Teachers.objects.create(name='zhang')
11 c = Clazz.objects.create(name='A1')
12 c.save()
13 c.teachers.add(t1)
14 c.teachers.add(t2)
15 c.teachers.add(t3)
16 c.save()
17 Students.objects.create(name='cici', age=7, clazz=c)
18 pass
19
20 def test_teachers_model(self):
21 t1 = Teachers.objects.get(name='liu')
22 self.assertEqual(t1.name, 'liu')
23 t2 = Teachers.objects.get(name='guan')
24 self.assertEqual(t2.name, 'guan')
25 t3 = Teachers.objects.get(name='zhang')
26 self.assertEqual(t3.name, 'zhang')
27 pass
28
29 def test_clazz_model(self):
30 t1 = Teachers.objects.get(name='liu')
31 t2 = Teachers.objects.get(name='guan')
32 t3 = Teachers.objects.get(name='zhang')
33 c = Clazz.objects.get(name='A1')
34 self.assertEqual(c.name, 'A1')
35 self.assertIn(t1, c.teachers.all())
36 self.assertIn(t2, c.teachers.all())
37 self.assertIn(t3, c.teachers.all())
38 pass
39
40 def test_students_model(self):
41 s = Students.objects.get(name='cici')
42 self.assertEqual(s.name, 'cici')
43 c = Clazz.objects.get(name='A1')
44 self.assertEqual(s.clazz, c)
45 pass
46
47 def tearDown(self):
48 pass
【代码分析】
在第07~18行代码中,在setUp()函数中分别对Teachers模型、Clazz模型和Students模型进行了初始化操作。
在第20~27行代码、第29~38行代码和第40~45行代码中,分别定义了一组测试方法test_teachers_model()、test_clazz_model()和test_students_model(),具体说明如下:
- 在第35~37行代码中,分别通过self调用assertIn()方法进行测试,用于判断3个教师模型对象t1、t2和t3是否包含在班级模型Clazz的教师字段teachers里。
- 在第44代码中,通过self调用assertEqual()方法进行测试,判断学生模型Students的外键clazz是否与班级模型Clazz的字段c一致。
下面,我们通过命令行指令python manage.py test app进行自动化测试,效果如图8.3所示。
图8.3 MyTestSite项目数据库自动化测试(1)
图8.3显示的是测试成功的情况,下面我们对【代码8-6】稍加修改,加入一些错误测试,具体代码如下:
【代码8-6】(详见源代码MyTestSite项目的testapp/tests.py文件)
01 from django.test import TestCase
02
03 from .models import Teachers, Clazz, Students
04
05 # Create your tests here.
06 class ModelTest(TestCase):
07 def setUp(self):
08 t1 = Teachers.objects.create(name='liu')
09 t2 = Teachers.objects.create(name='guan')
10 t3 = Teachers.objects.create(name='zhang')
11 c = Clazz.objects.create(name='A1')
12 c.save()
13 c.teachers.add(t1)
14 c.teachers.add(t2)
15 c.teachers.add(t3)
16 c.save()
17 Students.objects.create(name='cici', age=7, clazz=c)
18 pass
19
20 def test_teachers_model(self):
21 t1 = Teachers.objects.get(name='liu')
22 self.assertEqual(t1.name, 'liu')
23 t2 = Teachers.objects.get(name='guan')
24 self.assertEqual(t2.name, 'guan')
25 t3 = Teachers.objects.get(name='zhang')
26 self.assertEqual(t3.name, 'zhang')
27 pass
28
29 def test_clazz_model(self):
30 t1 = Teachers.objects.get(name='liu')
31 t2 = Teachers.objects.get(name='guan')
32 t3 = Teachers.objects.get(name='zhang')
33 c = Clazz.objects.get(name='A1')
34 self.assertEqual(c.name, 'A1')
35 self.assertIn(t1, c.teachers.all())
36 self.assertIn(t2, c.teachers.all())
37 self.assertNotIn(t3, c.teachers.all())
38 pass
39
40 def test_students_model(self):
41 s = Students.objects.get(name='cici')
42 self.assertEqual(s.name, 'cici')
43 c = Clazz.objects.get(name='A1')
44 self.assertEqual(s.clazz, c)
45 pass
46
47 def tearDown(self):
48 pass
【代码分析】
在第37行代码中,将通过self调用的assertIn()方法修改为assertNotIn()方法进行测试,判断教师模型对象t3是否不包含在班级模型Clazz的教师字段teachers里。
下面,我们再次通过命令行指令python manage.py test app进行自动化测试,效果如图8.4所示。因为教师模型对象t3包含在班级模型Clazz的教师字段teachers里,所以使用assertNotIn()方法进行测试时给出了相应的错误提示信息。
图8.4 MyTestSite项目数据库自动化测试(2)