什么是unittest
unittest是python自带的单元测试框架。
什么是单元测试?说白了就是测试代码。
测试代码中的哪些内容?测试类和方法。
所以unittest说白了就是测试代码中类和方法的测试框架。
为什么要用unittest
unittest是单元测试框架,与接口自动化测试有什么关系呢?
其实并没有直接的关系。只是unittest的强大功能很适合用来进行接口自动化测试。
详见下面的四大组件。
四大组件介绍
TestCase
即测试用例。什么是测试用例呢?一个完整的单元测试测试流程,包括环境的搭建(或前置条件)、测试代码、环境的还原(或后置条件)。单元测试的本质也就在这里,一个测试用例是一个完整的测试单元流程,通过运行这个测试单元,可以对某一个问题进行验证。
下面拿测试一个用户登录的接口进行举例说明,一条测试用户正常登录测试用例的完整流程包括:
1.环境的搭建。也即前置条件,每执行一条测试用例前应该做的事,如登录数据库;
2.测试用例代码。包括准备测试用例的数据(能登录成功的测试数据),准备预期结果,调用接口得到实际结果,预期结果与实际结果进行断言,判断这次的用例是否执行成功。
3.环境的还原。也即后置条件,每执行一条测试用例后应该做的事,如关闭数据库。
这一条只是其中一条测试用例。就测试用户登录接口而言还有其他的测试用例,如:用户名为空、密码为空、用户名错误、密码错误等。
TestSuite
即测试套件。把所有的测试用例集合起来,相当于一个篮子。也可以将多个测试套件集合起来。通过TestLoader来加载测试用例到测试套件中。
TestRunner
即测试用例的执行。前面TestSuite将所有测试用例集合起来,这里的TestRunner将执行测试套件下的所有测试用例。除了执行测试用例之外,还可以通过图形化的网页展示测试结果。
TestFixture
即测试夹具。包括测试环境的搭建和销毁,有前置条件和后置条件。
代码实操
TestCase与TestFixture均写在一个具体的测试用例模块中,如test_login.py、test_register.py等
TestCase
test_login.py
import unittest
#模拟登录接口
def login(username=None, password=None):
"""登录"""
if (not username) or (not password):
# 用户名或者密码为空
return {"msg": "empty"}
if username == 'tom' and password == '123456':
# 正确的用户名和密码
return {"msg": "success"}
return {"msg": "error"}
#定义测试类,继承TestCase类。最好以Test开头。
class TestLogin(unittest.TestCase):
"""
下面每个方法中的代码表示的是一条测试用例。一条测试用例包括四个部分:准备传参、调用接口获取实际结果、
准备预期结果、预期结果与实际结果进行断言
"""
#登录成功
def test_login_success(self):
#准备接口测试需的数据,即要传入接口的传参
username = 'tom'
password = '123456'
#用准备好的数据调用接口,拿到实际结果
actual_result = login(username,password)
#准备预期结果
expected_result = {"msg":"success"}
#预期结果及实际结果进行断言,判断这一条测试用例能否通过
self.assertTrue(actual_result == expected_result)
#登录失败--用户名+密码为空
def test_login_failed_empty(self):
#准备接口测试需的数据,即要传入接口的传参
username = ''
password = ''
#用准备好的数据调用接口,拿到实际结果
actual_result = login(username,password)
#准备预期结果
expected_result = {"msg": "empty"}
#预期结果及实际结果进行断言,判断这一条测试用例能否通过
self.assertTrue(actual_result == expected_result)
#登录失败--用户名错误
def test_login_failed_wrong_username(self):
#准备接口测试需的数据,即要传入接口的传参
username = 'aaa'
password = '123456'
#用准备好的数据调用接口,拿到实际结果
actual_result = login(username,password)
#准备预期结果
expected_result = {"msg": "error"}
#预期结果及实际结果进行断言,判断这一条测试用例能否通过
self.assertTrue(actual_result == expected_result)
# 登录失败--用户名正确,密码错误
def test_login_failed_wrong_password(self):
# 准备接口测试需的数据,即要传入接口的传参
username = 'tom'
password = '000000'
# 用准备好的数据调用接口,拿到实际结果
actual_result = login(username, password)
# 准备预期结果
expected_result = {"msg": "error"}
# 预期结果及实际结果进行断言,判断这一条测试用例能否通过
self.assertTrue(actual_result == expected_result)
右键选择unittest执行,可在控制台中看到执行结果。如果有一条测试用例不通过,会在左侧展示哪条不通过,且在右侧弹出AssertionError的报错;
如果是代码本身有错,则弹出代码具体的报错。
TestFixture
test_login.py
class TestLogin(unittest.TestCase):
#前置条件类方法(测试环境的准备),每执行所有方法前先执行该方法
@classmethod
def setUpClass(cls) -> None:
print("登录数据库")
#前置条件实例方法(测试环境的准备),每执行一条用例前先执行该方法
def setUp(self) -> None:
print("每执行一条用例前先执行的前置条件")
#后置条件实例方法(测试环境的销毁),每执行完一条用例后再执行该方法
def tearDown(self) -> None:
print("每执行一条用例后再执行的后置条件")
# 后置条件类方法(测试环境的准备),每执行所有方法前先执行该方法
@classmethod
def tearDownClass(cls) -> None:
print("关闭数据库")
#登录成功
def test_login_success(self):
#准备接口测试需的数据,即要传入接口的传参
username = 'tom'
password = '123456'
#用准备好的数据调用接口,拿到实际结果
actual_result = login(username,password)
#准备预期结果
expected_result = {"msg":"success"}
#预期结果及实际结果进行断言,判断这一条测试用例能否通过
self.assertTrue(actual_result == expected_result)
#...
#其他测试用例
执行unittest结果如下:
TestSuite与TestRunner均写在测试用例的运行模块:run.py中
TestSuite
TestSuite加载用例方式有两种,一种是加载某个目录下的所有测试用例,另一种是加载指定的测试用例。
第一种:加载所有测试用例(建议使用)
run.py
#初始化一个加载器:TestLoader
loader = unittest.TestLoader()
# 获取测试用例目录的路径
dir_path = os.path.dirname(os.path.abspath(__file__))
case_path = os.path.join(dir_path, 'tests')
# 初始化一个test_suite,使用 loader 收集所有的测试用例
test_suite = loader.discover(case_path)
第二种:加载部分测试用例
run.py
# 只加载注册的和登录的用例
suite_login = loader.loadTestsFromModule(test_login)
suite_register = loader.loadTestsFromModule(test_register)
# 初始化一个 suite
suit_total = unittest.TestSuite()
suit_total.addTests([suite_login, suite_register])
TestRunner
testRunner除了执行测试用例之外,还有一项任务:生成测试报告。测试报告的生成有两种方法:一种是使用unittest自带的TextTestRunner方法;另一种是使用第三方模块:HTMLTestRunner生成测试报告。所以测试方法的执行也有两种方式。
第一种:使用unittest自带的TextTestRunner
run.py
# 生成测试报告
with open("test_reports.txt", 'w', encoding='utf-8') as f:
runner = unittest.TextTestRunner(f)
runner.run(test_suit)
执行unittest后,在指定目录下生成test_reports.txt的测试报告如下。由于不好维护,且很难看,所以建议用第二种方法。
第二种:使用第三方模块:HTMLTestRunner(建议使用)
run.py
# HTML 测试报告 不是内置。是需要自己准备的。
from HTMLTestRunnerNew import HTMLTestRunner
with open("test_reports.html", 'wb') as f:
runner = HTMLTestRunner(
f,
title='测试报告',
description="这是第一次测试报告",
tester='wonderful'
)
runner.run(suit_total)
执行unittest后,生成测试报告如下: