接【实例:利用Django管理后台管理IP地址】(三)根据需求,定制数据展示
准备在views.py编写函数,多线程检测每个IP地址的占用情况。胡乱一通写完之后,哦豁,怎么测试写得对不对呢?
一开始想单独测试views.py文件,结果要引入各种包和配置文件,还要注意各种顺序,各种按照网上指导折腾了两三个小时还是一堆报错。
喝了口茶细想,最后函数是要放进框架里用的,现在的各种设置和引入还要注释掉的,长叹一口气,大可不必呀。
于是继续翻书,原来django有内置的单元测试框架unittest,真是有刀🔪不用非要用手劈柴。。。
坑🕳概述:在test.py中直接调用主函数,主函数里启用了多线程调用子函数,主线程能获取数据库数据,子线程却获取不到数据库数据。
梳理django默认测试行为:
1、执行全局的测试准备工作
2、在当前目录中查找名称匹配test*.py模式的测试文件
3、创建测试数据库
4、运行迁移,把模型和初始数据填充到测试数据库中
5、运行找到的测试
6、销毁测试数据库
7、执行全局的测试后的清理工作
简单描述我的代码实现:
1 #《views.py》 2 from mysiteapp.models 3 import ipaddr_infoimport subprocess 4 import threading 5 6 #子函数 7 def ip_status_alarm(ip): 8 print("子函数", ipaddr_info.objects.all()) 9 10 #主函数 11 def start_ping(ip): 12 print("主函数", ipaddr_info.objects.all()) 13 item=threading.Thread(target=ip_status_alarm, args=(ip,)) 14 item.start() 15 item.join() 16 17 #《test.py》 18 from django.test import TestCase 19 from mysiteapp import views 20 from mysiteapp.models import ipaddr_info 21 22 # Create your tests here. 23 class start_ping_test(TestCase): 24 def setUp(self): 25 ipaddr_info.objects.create(ipaddr="1.1.1.1", ipstatus="0", disconnect_alarm_num=5) 26 ipaddr_info.objects.create(ipaddr="2.2.2.2", ipstatus="1", connect_alarm_num=5) 27 print("开始", ipaddr_info.objects.all()) 28 29 def test_ping(self): 30 ip='1.1.1.1' 31 views.start_ping(ip) #从主函数进去,调用子函数的时候查无数据 32 # views.ip_status_alarm(ip) #直接从子函数进去,才有数据 33 34 def tearDown(self): 35 print("结束", ipaddr_info.objects.all())
执行31行views.start_ping(ip),注释掉32行输出结果:
开始 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
主函数 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
子函数 <QuerySet []>
结束 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
执行32行views.ip_status_alarm(ip),注释掉31行输出结果:
开始 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
子函数 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
结束 <QuerySet [<ipaddr_info: 1.1.1.1 0 5 0>, <ipaddr_info: 2.2.2.2 1 0 5>]>
解决方法:
替换23行class start_ping_test(TestCase): ,并按照提示导入包
class start_ping_test(TransactionTestCase):
原因:
真正的问题是这样的,start_ping_test继承自TestCase,TestCase将数据保留在内存中,并且不向数据库发出COMMIT(或者不会立即commit)。线程可能正在尝试直接连接到DB,而数据尚未提交到那里。所以在另外一个线程中看不到这些数据。Django提供的另一个测试基类(TransactionTestCase)可以解决这个问题。
1 TransactionTestCase和TestCase相同,除了将数据库重置为已知状态的方式以及测试代码测试提交和回滚的效果的能力。
2 TransactionTestCase通过截断所有表并重新加载初始数据来在测试运行之前重置数据库。
3 TransactionTestCase可以调用提交和回滚,并观察这些调用对数据库的影响。
附:执行测试命令
1. 测试项目中所有的应用
python3 manage.py test
2. 测试项目中单独的应用
python3 manage.py test app01
3. 运行项目中某个应用的测试文件中的一个Case
python3 manage.py test app01.test2.AuthorTestCase
4. 运行项目中某个应用的测试文件中的一个Case中的其中一个测试方法
python3 manage.py test app01.test2.AuthorTestCase.test_insert_data
5. 运行单元测试结束时不自动删除测试数据库(保留测试数据库)
python3 manage.py test app01 --keepdb