在发布会签到系统中使用django开发了发布会签到系统,
本文对该系统进行测试。
django.test
django.test
是Django框架中的一个模块,提供了用于编写和运行测试的工具和类。
django.test
模块包含了一些用于测试的类和函数,如:
-
TestCase
:这是一个基类,用于编写Django测试用例。继承自unittest.TestCase
,提供了一些额外的功能和方法,用于处理Django应用程序的测试环境。 -
SimpleTestCase
:这是一个更轻量级的测试基类,适用于没有数据库或网络访问的简单测试场景。 -
Client
:这是一个模拟HTTP请求的客户端类,用于在测试中模拟用户请求和验证响应结果。 -
RequestFactory
:这是一个用于创建HTTP请求对象的工厂类,用于在测试中生成HTTP请求实例。 -
其他辅助函数和装饰器,如
override_settings
用于在测试过程中临时覆盖Django设置,tag
用于给测试用例添加标签等。
通过使用django.test
模块,你可以编写单元测试、集成测试和功能测试等来验证和确保Django应用程序的正确性和稳定性。
下面是一个简单的示例代码,演示如何使用django.test
模块编写一个测试用例类:
from django.test import TestCaseclass MyTestCase(TestCase):def test_my_function(self):# 编写测试逻辑result = my_function()self.assertEqual(result, expected_result)
总结来说,django.test
模块提供了一套用于编写和运行Django应用程序测试的工具和类,能够帮助开发者验证和确保应用程序的正确性和稳定性。
测试index视图
import os,django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "guest.settings")
import django
django.setup()
from django.test import TestCase
class IndexPageTest(TestCase):def test_index_page_renders_index_template(self):response = self.client.get("/index/")self.assertEqual(response.status_code,200)self.assertTemplateUsed(response,'index.html')
测试类徐亚集成TestCase,使用client实例可以请求get和post HTTP请求
获取response后断言状态码,
并使用assertTemplateUsed方法断言该请求是否使用index.html模板
测试login视图
import osos.environ.setdefault("DJANGO_SETTINGS_MODULE", "guest.settings")from django.contrib.auth.models import User
from django.test import TestCase
class LoginActionTest(TestCase):def setUp(self) -> None:User.objects.create_user("admin1","admin@mail.com","admin123456") #创建用户def test_add_admin(self):user=User.objects.get(username="admin1")#查询self.assertEqual(user.username,"admin1")self.assertEqual(user.email, "admin@mail.com")def test_login_action_username_password_null(self):"""测试密码为空"""test_data={'username':'','password':''}response=self.client.post('/login/',data=test_data) #使用self的client可以对urls进行测试self.assertEqual(response.status_code,200)self.assertIn(b"username or password error",response.content)def test_error_password(self):test_data = {'username': 'abc', 'password': ''}response = self.client.post('/login/', data=test_data) # 使用self的client可以对urls进行测试self.assertEqual(response.status_code, 200)self.assertIn(b"username or password error", response.content)def test_login_success(self):test_data = {'username': 'admin', 'password':'admin123456'}response = self.client.post('/login/', data=test_data) # 使用self的client可以对urls进行测试self.assertEqual(response.status_code, 302)
在第一个测试方法中对User进行了测试,查询数据库是否新建了该账号
在2,3,4测试方法里则是测试了/login/请求,通过传入不同的参数,校验响应。
注意b:在Python中,字符串前面加上b
表示这是一个字节字符串(bytes string)。字节字符串是一种以字节为单位表示的字符串,它可以包含任意的二进制数据。
在上述代码中,response.content
被认为是一个字节字符串。将b"username or password error"
作为参数传递给elf.assertIn()
函数,表示期望的"username or password error"字符串是以字节的形式存在的。
使用字节字符串的一个常见场景是在进行文本匹配或者通过网络传输二进制数据时,通常需要使用字节字符串来处理二进制数据以及非ASCII字符集。
与之相对,普通的字符串(字符字符串)在Python中使用引号括起来,例如"username or password error"
。它们是以Unicode字符为单位表示的字符串,适用于大多数文本处理任务。
需要注意的是,对于编写测试用例来说,根据实际情况选择使用字节字符串或字符字符串,确保与被测试代码中的数据类型一致。
测试模型
from datetime import datetime
import os,django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "guest.settings")# project_name 项目名称
django.setup()
from django.test import TestCase
from sign.models import Event,Guestfrom django.test.utils import setup_test_environment
setup_test_environment()
time=datetime.now()
class ModelTest(TestCase):def setUp(self) -> None:Event.objects.create(id=4,name="oneplus 3 event",status=True,limit=2000,address="shenzhen",start_time=time)Guest.objects.create(id=4, event_id=4,realname="zhangsan",phone="13276766666",email="zhangsan@qq.com",sign=False,create_time=time)def test_event_models(self):result=Event.objects.get(name="oneplus 3 event")self.assertEqual(result.address,"shenzhen")self.assertTrue(result.status)def test_guest_models(self):result=Guest.objects.get(phone="13276766666")self.assertEqual(result.realname,"zhangsan")self.assertFalse(result.sign)
测试模型时需要先在setup里创建模型,然后在测试方法里调用查询方法校验是否创建成功,
测试event_manage
import os,django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "guest.settings")
django.setup()
from datetime import datetimefrom django.contrib.auth.models import Userfrom sign.models import Event
time=datetime.now()import django
django.setup()
from django.test import TestCase
class EventManageTest(TestCase):def setUp(self) -> None:"""测试event_manage和搜索方法,需要先登录,所以在setup里先登录"""User.objects.create_user("admin1", "admin@mail.com", "admin123456")test_data = {'username': 'admin', 'password': 'admin123456'}response = self.client.post('/login/', data=test_data)Event.objects.create(id=4, name="oneplus 3 event", status=True, limit=2000, address="shenzhen", start_time=time)def test_event(self):response = self.client.get("/event_manage/")self.assertEqual(response.status_code, 200)self.assertIn(b"oneplus", response.content) #response.contentself.assertIn(b"shenzhen", response.content)def test_search(self):"""测试搜索"""testdata={"search_name":"oneplus"}response = self.client.get("/search_name/",data=testdata)self.assertEqual(response.status_code, 200)self.assertIn(b"oneplus", response.content) #response.contentself.assertIn(b"shenzhen", response.content)
因为event_manage请求在登录后才能调用,所以在setup里请求了登录接口。
在测试方法里调用了对应接口,并校验结果。
测试签到功能
import os,django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "guest.settings")
django.setup()
from datetime import datetimefrom django.contrib.auth.models import Userfrom sign.models import Event, Guesttime=datetime.now()import django
django.setup()
from django.test import TestCase
class SignTest(TestCase):def setUp(self) -> None:"""测试event_manage和搜索方法,需要先登录,所以在setup里先登录"""User.objects.create_user("admin1", "admin@mail.com", "admin123456")test_data = {'username': 'admin', 'password': 'admin123456'}response = self.client.post('/login/', data=test_data)Event.objects.create(id=5, name="oneplus 3 event", status=True, limit=2000, address="shenzhen", start_time=time)Event.objects.create(id=4, name="oneplus 3 event", status=True, limit=2000, address="shenzhen", start_time=time)# Event.objects.create(id=4, name="oneplus 3 event", status=True, limit=2000, address="shenzhen", start_time=time)Guest.objects.create(id=4, event_id=4, realname="lisi", phone="13276766666", email="lisi@qq.com",sign=False,create_time=time)Guest.objects.create(id=5, event_id=5, realname="lisi", phone="13276609878", email="lisi@qq.com",sign=True,create_time=time)def test_sign_1(self):response = self.client.get("/sign_index/4/")print(response.content)self.assertEqual(response.status_code, 200)self.assertIn(b"oneplus 3 event", response.content) #response.contentdef test_sign_phone_error(self):response = self.client.post("/sign_index_action/4/",{"phone":"1321761766666"})self.assertEqual(response.status_code, 200)self.assertIn(b"phone error", response.content) #response.contentdef test_sign_phone_not_match(self):response = self.client.post("/sign_index_action/4/",{"phone":"13276609878"})self.assertEqual(response.status_code, 200)self.assertIn(b"event id or phone error", response.content) #response.contentdef test_sign_phone_success(self):response = self.client.post("/sign_index_action/4/",{"phone":"13276766666"})self.assertEqual(response.status_code, 200)self.assertIn(b"sign in success", response.content) #response.contentdef test_sign_phone_has_sign(self):response = self.client.post("/sign_index_action/5/",{"phone":"13276609878"})self.assertEqual(response.status_code, 200)self.assertIn(b"user has sign in", response.content) #response.content
因为签到功能需要先有发布会和客户,所以需要在setup里先创建对应模型,然后在测试方法中传入不同的参数,校验结果。
可以看到使用django.test进行测试,即测试了网页路由,也测试了对应的视图函数。
测试数据库
在Django中,当你运行测试用例时,测试框架会自动为你创建一个专用的测试数据库,该数据库是在测试运行期间被使用的临时数据库。当测试完成后,测试框架会清除该数据库。
在进行模型测试时,如果你在测试用例中创建了模型实例并保存到数据库中,测试框架会确实在测试数据库中创建相应的表以及保存模型对象的记录。
示例代码:
from django.test import TestCase
from myapp.models import MyModelclass ModelTestCase(TestCase):def test_create_model(self):# 创建并保存模型对象MyModel.objects.create(name="Example")# 断言模型对象是否在测试数据库中存在self.assertTrue(MyModel.objects.exists())
当你运行上述测试用例时,测试框架会在测试数据库中创建一个MyModel
表,并将模型对象保存在该表中。注意,测试中的模型数据不会影响你的开发或生产环境数据库,它们只是在测试期间使用的临时数据。
需要注意的是,测试数据库是独立于你的开发或生产数据库的,并且在每次运行测试时都会自动创建和销毁。这样可以确保测试的可靠性和独立性。
如图,在运行发布会测试文件时,显示创建了数据库,在测试结束时,数据库被销毁。
所以在进行测试时,用户和数据库表等信息,都需要创建。
如果在运行测试,发现状态码返回是302,需要注意是否创建用户,并使用该用户登录。
运行测试
python manage.py test tests
可以使用tests目录下的所有测试文件
可以看到运行了16个用例都通过了
测试覆盖率
测试覆盖率是一种度量软件测试程度的指标,它衡量了测试代码中有多少部分被执行了。
要计算测试覆盖率,可以使用以下公式:
测试覆盖率 = (已执行的代码行数 / 总代码行数) * 100
具体的计算步骤可以分为以下几个步骤:
-
确定要计算覆盖率的代码范围。这可以是整个项目、单个文件、单个函数等。
-
运行测试套件。执行针对代码范围的测试,确保已经执行了尽可能多的代码路径。
-
收集执行信息。使用测试覆盖率工具(如
coverage
)来跟踪代码的执行情况,并记录已经执行的代码行。 -
统计覆盖率数据。根据执行信息统计已经执行的代码行数和总代码行数。
-
计算覆盖率百分比。根据统计的数据,使用上述公式计算测试覆盖率百分比。
测试覆盖率可以根据需要进行细分,如语句覆盖率(Statement Coverage)、分支覆盖率(Branch Coverage)、条件覆盖率(Condition Coverage)等。具体的计算方式和度量指标可能会有所不同,但基本原理是相似的。
需要注意的是,测试覆盖率并不能完全衡量代码的质量,它只能指示测试是否已经覆盖了一定程度的代码。在计算测试覆盖率时,应该根据项目的实际情况和需求来确定合适的目标和阈值。
总结来说,测试覆盖率是通过统计已执行的代码行数与总代码行数之比来计算的。使用测试覆盖率工具可以帮助收集和分析执行信息,从而得出测试覆盖率的百分比。测试覆盖率可以帮助评估测试的完整程度,但它并不能完全代表代码的质量。
coverage获取测试覆盖率
在Python中,你可以使用第三方库 coverage
来获取测试覆盖率数据。coverage
是一个广泛使用的代码覆盖率工具,可以统计你的测试用例对代码的覆盖情况。
下面是一个简单的示例,展示了如何使用 coverage
来获取测试覆盖率数据:
-
安装
coverage
库:pip install coverage
-
在终端中运行测试并收集覆盖率数据:
这里
myapp
是你要测试的应用程序的名称。--source
参数用于指定要收集覆盖率数据的代码路径。 -
生成覆盖率报告:
coverage report
运行该命令后,
coverage
将会产生一个简单的文本报告,显示每个文件和每个文件中被测试的代码行的覆盖情况。
你也可以使用 coverage html
命令生成一个 HTML 格式的覆盖率报告,该报告能够更直观地展示代码覆盖情况。
注意:在运行测试之前,确保你的代码已经被正确安装和配置,并且拥有相应的测试用例。
总结来说,通过使用 coverage
库,你可以轻松地获取你的Python代码的测试覆盖率数据,并生成相应的报告来帮助你分析和评估你的测试质量。
运行coverage
coverage run --source=sign manage.py test
这里–source后面是目录,意思是统计sign目录下文件的代码执行覆盖率,即sign下每个文件里的代码在执行时运行了几行。
可以看到,apps里代码一共3行,miss 3行,说明3行代码都没有执行到,
总的测试覆盖率是95%,也就是代码行覆盖率是95%
也可以不指定目录,则会计算根路径下所有文件代码执行覆盖率。
coverage run manage.py test