Django单元测试

单元测试

​ 单元测试是实际开发中很重要但也经常被忽视的部分,其主要原因是编写Web功能测试所耗费的时间可能会大于你开发此功能的时间,因此,对于需要快速开发、上线的业务来说,项目中关于单元测试的部分很少。但是对于需要长期维护的项目,还需要考虑增加单元测试。只是第一次编写时会比较耗费时间,一旦基础结构完成,后续跟着功能的增加来增加单元测试并不会耗费多少时间,但是收益却是十分明显的。

​ 单元测试的主要目的是让代码更健壮,尤其是在进行重构或者业务增加的时候,跑通单元测试,就意味着新加入的代码或者修改的代码没有问题。在实际开发中,单元测试的覆盖率没有那么高,其主要原因也是写单元测试的成本过高,尤其是对于很复杂的业务。下面就Django内置的测试工具进行说明。

TestCase中几个方法的说明

​ 在Django中运行测试用例时,如果我们使用的SQLite数据库,Django会帮助我们创建一个基于内存测试的数据库,用于测试。

​ 但是对于MySQL数据库,Django会直接用配置的数据库用户和密码创建一个名为test_student_db的数据库,用于测试。因此需要保证有建表和建库的权限

​ 当然,也可以自定义测试用的数据库名称,通过setting配置:

1DATABASE = { 2 'default': { 3 'ENGINE': 'django.db.backends.mysql', 4 'USER': 'mydatabaseuser', 5 'NAME': 'mydatabase', 6 'TEST': { 7 'NAME': 'mytestdatabase', # 这里配置 8 }9 }10}

Django提供了一个名为TestCase的基类,我们可以通过继承这个类来实现自己的测试逻辑。TestCase为我们系统了以下方法(需要用到的):

  • def setUp(self):用来初始化环境,包括创建初始化的数据,或者做一些其他准备工作。
  • def test_xxxx(self):方法后面的xxxx可以是任意东西。以test_开头的方法,会被认为是需要测试的方法,跑测试时会被执行,每个需要被测试的方法是相互独立的。
  • def tearDown(self):跟setUp相对,用来清理测试环境和测试数据。

样例代码:

Model层测试

主要保证数据的写入和查询是可用的,同时也需要保证我们在model所提供的方法是复合预期的。

比如在Model中增加了sex_show这样的属性,用来展示sex这个字段的中文显示,而不是1,2。当然,这个功能是Django已经提供给我们的。

1from django.db import models 2 3# Create your models here. 4 5class Student(models.Model): 6 SEX_ITEMS = [ 7 (1, '男'), 8 (2, '女'), 9 (0, '未知'), 10 ] 11 STATUS_ITEMS = [ 12 (0, '申请'), 13 (1, '通过'), 14 (2, '拒绝'), 15 ] 16 name = models.CharField(max_length=128, verbose_name='姓名') 17 sex = models.IntegerField(choices=SEX_ITEMS, verbose_name='性别') 18 profession = models.CharField(max_length=128, verbose_name='职业') 19 email = models.EmailField(verbose_name='Email') 20 qq = models.CharField(max_length=128, verbose_name='QQ') 21 phone = models.CharField(max_length=128, verbose_name='电话') 22 23 status = models.IntegerField(choices=STATUS_ITEMS, default=0, verbose_name='审核状态') 24 created_time = models.DateTimeField(auto_now_add=True, editable=False, verbose_name='创建时间') 25 26 def __str__(self): 27 return '<Student: ()>'.format(self.name) 28 29 class Meta: 30 verbose_name = verbose_name_plural = '学员信息' 31 32 @property 33 def sex_show(self): 34 return dict(self.SEX_ITEMS)[self.sex] 35 36 @classmethod 37 def get_all(cls): 38 return cls.objects.all()

views.py同级目录下的test.py,它是App初始化时Django默认帮我们创建的

1from django.test import TestCase, Client 2from .models import Student 3 4# Create your tests here. 5class StudentTestCase(TestCase): 6 def setUp(self) -> None: 7 Student.objects.create( 8 name='test', 9 sex=1, 10 email='test@test.com', 11 profession='test职业', 12 qq='123', 13 phone='123456', 14 ) 15 16 def test_create_and_sex_show(self): 17 student = Student.objects.create( 18 name='zhangsan', 19 sex=1, 20 email='zhangsan@test.com', 21 profession='程序员', 22 qq='123123', 23 phone='12121212', 24 ) 25 self.assertEqual(student.sex_show, '男', '性别字段内容更展示不一致') 26 27 def test_filter(self): 28 Student.objects.create( 29 name='lisi', 30 sex=1, 31 email='lisi@test.com', 32 profession='程序员', 33 qq='2124325', 34 phone='11111111' 35 ) 36 name='test' 37 students= Student.objects.filter(name=name) 38 self.assertEqual(students.count(), 1, '应该只存在一个名称为{}的记录'.format(name)) 39

在setUp中,创建了一条数据用于测试。test_create_and_sex_show用来测试数据创建以及sex字段的正确展示,test_filter测试查询是否可用。

需要说明的是,每一个以test_开头的函数都是独立运行的。因此,setUp和tearDown也会在每个函数运行时被执行。简单理解就是每个函数都处于独立的运行环境。

View层测试

test.py 添加如下代码

1 def test_get_index(self): 2 # 测试首页的可用性 3 client = Client() 4 response = client.get('/') 5 self.assertEqual(response.status_code, 200, 'status code must be 200!') 6 7 def test_post_student(self): 8 client = Client() 9 data = dict( 10 name='test_for_post', 11 sex=1, 12 email='333@test.com', 13 profession='程序员', 14 qq='333', 15 phone='222', 16 ) 17 response=client.post('/', data) 18 self.assertEqual(response.status_code, 302, 'status code must be 302!') 19 20 response = client.get('/') 21 self.assertEqual(b'test_for_post' in response.content, 'response content must contain `test_for_post`')
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

​一篇文章总结一下Python库中关于时间的常见操作

前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在