测试例子部分源于官方文档,部分为自己碰到的案例,官方文档链接:
https://docs.djangoproject.com/en/2.2/topics/testing/
除了官方文档提到的内容,本文主要写一下自己使用过程中碰到的问题和解决方法
0X01 如何编写单元测试
Python有unittest库来进行单元测试,Django的单元测试是基于unittest库的,只不过在unittest的基础上进行了封装,并提供了一系列单元测试工具,来帮助你进行测试。Django在创建APP之后,默认会在APP目录下创建一个tests.py文件,这里就是存放你测试代码的地方,当然,如果需要测试的内容多了,都放在一个文件中显然不合理,所以Django提供高级玩法,模块化测试,这个后面再说,先上一个例子:
from django.test import TestCase
from myapp.models import Animal
class AnimalTestCase(TestCase):
def setUp(self):
Animal.objects.create(name="lion", sound="roar")
Animal.objects.create(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
lion = Animal.objects.get(name="lion")
cat = Animal.objects.get(name="cat")
self.assertEqual(lion.speak(), 'The lion says "roar"')
self.assertEqual(cat.speak(), 'The cat says "meow"')
上面就是一个最基础的测试用例,你所编写的测试类需要继承于TestCase,当你运行测试时,Django会找到所有的以test开头的文件,运行这些文件中所有父类是TestCase的类中的测试方法,测试方法为以test开头的函数。
上面的例子中,setUp函数会在运行test_xxxx函数前被调用,可以进行一些初始化操作,例如创建测试数据,获取Token等。
0X02 运行单元测试
在Django项目目录下使用manage.py来运行单元测试,该命令会运行所有APP的所有tests.py中的所有测试:
python manage.py test
运行某个package下的所有测试:
python manage.py test package_name
运行某个package下的某个测试类(TestCase):
python manage.py test pack.tests.xxxTestCase
运行某个package下的某个测试类的某个测试方法:
python manage.py test pack.tests.xxxTestCase.test_animals_can_speak
上面提到过,默认会匹配所有以test_func开头的测试方法,你也可以自己制定测试方法的正则:
python manage.py test --pattern="tests_*.py"
在运行单元测试的时候,可以使用ctrl+c来结束测试,测试程序将等待当前正在运行的测试结束,然后正常退出。在退出期间,测试程序会和往常一样输出测试失败的详细信息、运行了多少测试、出现的错误和故障,并且会像往常一样销毁测试数据库。
连续按下两次ctrl+c将会强制退出测试程序,此时会缺少一些详细信息,也不会销毁测试数据库,慎用。
0X03 测试数据库
需要和model交互的话会需要用到数据库,但Django不会使用生产环境的数据库进行测试,会创建单独的测试数据库,例如settings.py中定义的连接的数据库为product_db,那么默认创建的测试数据库就为test_product_db。
上面提到过,在执行单元测试命令后,会自动创建测试数据库,结束测试后会自动销毁测试数据库。每次都去创建和销毁比较耗时,如果不需要销毁测试数据库可以携带keepdb参数,不销毁数据库:
python manage.py test --keepdb
如上一节所述,如果强制中断测试运行,则可能不会销毁测试数据库。在下一次运行时,将询问您是否要重用或销毁数据库。使用该选项可以禁止该提示并自动销毁数据库。例如,在连续集成服务器上运行测试时,这可能很有用,例如,测试可能会因超时而中断:
python manage.py test --noinput
默认Django settings中指定的是sqlite数据库,那么测试数据库将创建在内存中,不会使用文件系统。在settings.py中的DATABASES提供了很多关于测试数据库的选项,例如指定测试数据库的名字:、
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'USER': 'mydatabaseuser',
'NAME': 'mydatabase',
'TEST': {
'NAME': 'mytestdatabase',
更多字段:https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-TEST_NAME
有一个地方暂时没有捋清楚什么意思,如果有兄弟知道可以分享下,原文:https://docs.djangoproject.com/en/2.2/ref/applications/#django.apps.AppConfig.ready
Finding data from your production database when running tests?
If your code attempts to access the database when its modules are compiled, this will occur before the test database is set up, with potentially unexpected results. For example, if you have a database query in module-level code and a real database exists, production data could pollute your tests. It is a bad idea to have such import-time database queries in your code anyway - rewrite your code so that it doesn’t do this.
This also applies to customized implementations of ready().
0X04 关于Model测试
涉及到Model的测试,有一个需要注意的地方,每个测试方法都是独立的,比如编写了以下测试类,在测试方法A创建的对象,用测试方法B去获取,是获取不到的,在测试方法A结束后该对象就被销毁了,具体例子:
class UserTestCase(TestCase):
def setUp(self):
UserInfo.objects.create(username='sup_user', password='123456')
def test_add_user(self):
UserInfo.objects.create(username='user', password='123456')
def test_sup_user_exist(self):
self.assertEqual(UserInfo.objects.filter(username='sup_user').count(), 1)
def test_user_exist(self):
self.assertEqual(UserInfo.objects.filter(username='user').count(), 1)
上述代码在setUp方法中创建了sup_user用户,在test_add_user方法中创建了user用户,然后test_sup_user_exist方法测试sup_user是否存在,test_user_exist方法测试user用户是否存在,测试结果如下:
可以看出来,setUp方法创建的对象生命周期是整个测试类的运行过程,test方法中的对象生命周期仅仅是这个方法。当然本类setUp创建的对象,在其他类也是查询不到的,数据库一直为空,实测得出的结论。
0X05 API的测试
Django提供了Client来进行API测试,https://docs.djangoproject.com/en/2.2/topics/testing/tools/
>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
0X06 执行测试的顺序
1.首先执行所有TestCase的子类
2.接着执行SimpleTestCase和TransactionTestCase的子类
3.最后执行unittest.TestCase和doctests
0X07 rest_framework单元测试
实际场景中经常会用到rest_framework和rest_framework_jwt。那么所有的API都是需要进行身份验证的,那么该如何进行单元测试呢?官方文档:https://www.django-rest-framework.org/api-guide/testing/
1.APIRequestFactory
DjangoRequestFactory的子类,具体的参数和用法查阅文档即可,下面是一个DEMO:
from rest_framework.test import APIRequestFactory
class InterfaceTestCase(TestCase):
def setUp(self):
self.user = UserInfo.objects.create_user(username='sup_admin', password='123456')
def test_list_method(self):
factory = APIRequestFactory()
request = factory.get('/interfaces/')
view = VIEWSET_CONF['interface_list'] # viewset.as_view({'get': 'list'})
force_authenticate(request, user=self.user)
response = view(request)
self.assertEqual(response.status_code, 200)
首先创建APIRequestFactory对象,调用get方法,返回一个WSGIRequest对象,实例化处理请求的视图函数对象并调用,返回response。可以看到在setUp方法里我创建一个用户,然后在test方法里使用force_authenticate进行强制身份验证,这样就可以正常访问视图了。
2.APIClient
扩展了Django的Client,DEMO:
from rest_framework.test import APIClient
class InterfaceTestCase(TestCase):
def setUp(self):
self.user = UserInfo.objects.create_user(username='sup_admin', password='123456')
self.client = APIClient()
self.client.force_authenticate(self.user)
def test_list_method(self):
response = self.client.get('/interfaces/')
self.assertEqual(response.status_code, 200)
文档中提到了使用client.login方法来通过验证,但是该方法只支持SessionAuthntication,JWT的话不支持。
self.client.login(username=self.user.username, password=self.user.password)
3.RequestsClient
有别于上面提到的所有方法,该方法是完全模拟一个外部请求的请求过程,使用的是Python的requests库,这个使用起来注意事项很多,有需求自行查阅文档
测试例子部分源于官方文档,部分为自己碰到的案例,官方文档链接:https://docs.djangoproject.com/en/2.2/topics/testing/除了官方文档提到的内容,本文主要写一下自己使用过程中碰到的问题和解决方法0X01 如何编写单元测试Python有unittest库来进行单元测试,Django的单元测试是基于unittest库的,只不过在unittest...
Django里的单元测试何为单元测试Django.test单元测试样例参考文献
何为单元测试
单元测试是用来对一个模块、一个函数、一个接口或者一个类来进行正确性检验的自动测试工作。
比如对于函数max(a,b),我们可以编写如下的测试用例:
输入a=5,b=10,期待返回b;
输入a=10,b=5,期待返回a;
输入a=-5,b=0,期待返回b;
输入a=b,期待返回a或b;
输入非数值类型,期...
本文将会讨论什么是单元测试、单元测试的意义、django中是如何进行单元测试的
认识单元测试
单元测试就是用一段代码去测试另一段代码。测试的目标是软件设计的最小单位,比如函数或者一个类的方法。它的测试用例是基于白盒测试来设计的(知道程序设计的内部逻辑结构),也就是说单元测试的目的在于发现各模块内部可能存在的各种错误。
单元测试的意义
Django框架学习(四):
Django 单元测试编写
单元测试
Django 框架自带了
单元测试工具,利用该工具,我们可以方便地对单元进行错误检查,以提高项目的质量。
编写
单元测试
在
Django 框架中,当新建一个应用时,会默认新建一个用于
单元测试的 test.py 文件,我们的
单元测试代码就写在 test.py 里。
比如,在 view.py 里,我们写了一个相应用户登录操作的接口,如下:
client模块的简单介绍
Client 是可以充当客户端用于测试的类. 可以发起Get 和Post 请求, 并能获取响应.
响应对象是请求过程中呈现的上下文和模板详细信息, 但不是最后浏览器渲染后展现给用户的, 所以不能代替浏览器的渲染.
Client的实例对象是能够实现状态保持的,在销毁Client对象前会一直携带Cookie.
默认情况下,test client会忽略CSRF检查,如果要强制进行CSRF检查,可以
csrf_client = Client(enforce_csrf_checks=Tru
1、html里的东西不能测。
Html里的HTML代码大部分都是写死的,嵌套在html中的
Django模板语言也不能测,即使有部分逻辑。但写
测试用例时至少要调用一个类或者方法。模板语言没有出参也没有入参,不能测
2、models模型可测。属于
数据库层
3、views视图层可以测。有入参、有方法。
综上:根据
Django语言特点,可测models和views
二.
Django单元测试具体步骤
1、在创建
Django app时
为了进一步提高提测质量,了解到Django有自带的单元测试,一行简单的代码就能测试所有(或指定)app下tests.py文件中的测试逻辑。总体来说不难,但研究过程中也踩过无数个坑(在正文中慢慢道来),在此记录,欢迎大家一起讨论。
框架运行步骤
在终端执行命令,启动测试
框架首先初始化__init__以及类变量
根据生产数据库,拷贝一份test数据库(不会拷贝数据)
运行以test开头的方...