unittest
Python中的一个单元测试框架
unittest的五大要素
- TestCase 测试用例
- TestSuite 测试套件,将多个用例组织在一起
- TextTestRunner 套件运行
- TestLoader 导入多个模块的方法形成测试套件
- Fixture 固定装置,保证前置/后置代码一定被执行
TestCase
说明:测试用例 使用:
1.导包:import unittest 2.定义测试类,继承unittest。TestCase 3.定义测试方法:方法名必须以test开头
运行:
1.运行一个测试方法:将光标定在要运行的方法上 2.运行一个类中的所有方法:将光标定在类名上 注:只有test开头的方法才能自动运行
演示:
import unittest
class Test1(unittest.TestCase):
def test01(self):
print("hello testcase")
def test02(self):
print("hhhh~")
def _test03(self):
print("不是test开头的")
将光标放在test01方法上右键执行,只执行了一个测试方法 将光标放在类上,执行了两个以test开头的方法,没有执行非test开头的方法
补充:如果发现运行不了,可能是有多个测试框架的原因,可以在Settings中设置默认运行的测试框架
TestSuite
说明: 测试套件,多个用例集合在一起就是一个测试套件 使用:
1.导包:import unittest 2.实例化:suite=unittest.TestSuite 3.添加测试用例: 添加单个测试用例 suite.addTest() 添加多个测试用例 suite.addTest(unittest.makesuite(类名))
执行: 执行需要用到TextTestRunner这个类,在下面有讲到 演示:
import unittest
from test1 import Test1
suite = unittest.TestSuite()
suite.addTest(Test1("test01"))
suite.addTest(Test1("test02"))
suite.addTest(Test1("_test03"))
runner = unittest.TextTestRunner()
runner.run(suite)
可以选择一个一个手动导入测试用例,可以导入非test开头的方法,运行效果如下 但这会非常麻烦,推荐使用suite.addTest(unittest.makesuite(类名)),一次导入一个类中以test开头的方法,运行效果如下 看起这个导入方式比一个个导入高效了许多,但是依旧存在缺点,但就是当要导入的类多了之后导包就会非常多,不简洁且浪费时间。所以推出了一个更加高效的测试套件组织方式TestLoader,在下面会讲到
TextTestRunner
说明: 以文本的形式执行测试套件 使用:
1.导包: import unittest 2.实例化:runner = unittest.TextTestRunner() 3执行:runner.run(测试套件)
演示: 见上
TestLoader
说明: 搜索指定目录下的指定py文件,在py文件中搜索测试用例,并将这些用例添加到测试套件中,返回一个测试套件 使用:
1.导包: import unittest 2.实例化并调用discover()方法: suite=unittest.TestLoader().discover(目录,pattern="指定开头*.py“)
演示:
import unittest
suite =unittest.TestLoader().discover("./package1",pattern = "test*.py");
runner = unittest.TextTestRunner()
runner.run(suite)
package1包中的内容:
运行效果如下
补充: TestLoader 使用时也可以这样用:
suite=unittest.defaultTestLoader.discover(目录,pattern="指定开头*.py“)
defaultTestLoader其实就是TestLoader的实例化,使用区别就是不需要加括号了,超级适合忘记加括号的同学使用,源码如下
Fixture
fixture主要用于对一个测试用例环境的初始化和销毁,包括一个初始化和一个销毁方法(不一定要一起用,用一个也可以~)
fixture有三种级别:
1.函数级别:每一个测试方法都会执行
setup() teardown()
2.类级别:每个类运行都会执行
setUpClass() tearDownClass()
3.模块级别:一个模块运行一次
setUpModule() tearDownModule()
演示:
import unittest
def setUpModule():
print("setupModule")
def tearDownModule():
print("tearDownModule")
class Test04(unittest.TestCase):
def setUp(self) -> None:
print("setup")
def tearDown(self) -> None:
print("teardown")
@classmethod
def setUpClass(cls) -> None:
print("setUpClass")
@classmethod
def tearDownClass(cls) -> None:
print("teardownclass")
def test01(self):
print("tes01")
def test02(self):
print("test02")
结果如下
unittest断言
断言是什么: 让程序代替人工判断执行结果和预期结果是否相同
为什么需要断言: 自动化脚本执行都是无人值守的,需要通过断言判断脚本执行结果是否符合预期
序 号 | 断言方法 | 断言描述 |
---|
1 | assertEqual(arg1, arg2, msg=None) | 验证arg1=arg2,不等则fail | 2 | assertNotEqual(arg1, arg2, msg=None) | 验证arg1 != arg2, 相等则fail | 3 | assertTrue(expr, msg=None) | 验证expr是true,如果为false,则fail | 4 | assertFalse(expr,msg=None) | 验证expr是false,如果为true,则fail | 5 | assertIs(arg1, arg2, msg=None) | 验证arg1、arg2是同一个对象,不是则fail | 6 | assertIsNot(arg1, arg2, msg=None) | 验证arg1、arg2不是同一个对象,是则fail | 7 | assertIsNone(expr, msg=None) | 验证expr是None,不是则fail | 8 | assertIsNotNone(expr, msg=None) | 验证expr不是None,是则fail | 9 | assertIn(arg1, arg2, msg=None) | 验证arg1是arg2的子串,不是则fail | 10 | assertNotIn(arg1, arg2, msg=None) | 验证arg1不是arg2的子串,是则fail |
unittest跳过
注解 | 解释 |
---|
@unittest.skip(“跳过原因”) | 一般用于未实现的用例 | @unittest.skipIf(条件,原因) | 条件满足就不执行 |
HTML报告
脚本执行完毕之后,还需要看到HTML报告,通过HTMLTestRunner.py 来生成测试报告。 HTMLTestRunner.py 文件需要自己手动导入,一般放在tools包中 HTMLTestRunner.py 文件有需要的自取:HTMLTestRunner.py 使用:
curpath=sys.path[0]
now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))
if not os.path.exists(curpath+'/resultreport'):
os.makedirs(curpath+'/resultreport')
filename=curpath+'/resultreport/'+now+'resultreport.html'
with open(filename,'wb') as fp:
runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'测试报
告',description=u'用例执行情况',verbosity=2)
suite=unittest.defaultTestLoader.discover('../test',pattern='test*.py',top_level_dir=None)
runner.run(suite)
执行后会生成一个这样的HTML报告
数据驱动DDT
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成,这个需要手动安装 cmd安装
pip install ddt
校验是否安装成功
pip show ddt 显示版本号就成功了
使用:
1.导包 from ddt import ddt, unpack, data, file_data 2.在测试类上注释@ddt 3.在测试方法上注释要使用的驱动方法,例如@data
DDT的主要方法:
@data(): 装饰测试方法。参数是一系列的值。
@file_data(): 装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。 注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件 处理。 如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。 如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。
@unpack(): 传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列 表对应到多个参数上。字典也可以这样处理。
演示:
import time
import unittest
from ddt import ddt, unpack, data, file_data
from selenium import webdriver
@ddt
class Test05(unittest.TestCase):
def setUp(self) -> None:
self.driver = webdriver.Chrome()
self.driver.get("https://www.baidu.com/")
self.driver.maximize_window()
self.driver.implicitly_wait(30)
def tearDown(self) -> None:
self.driver.quit()
@data("python", "selenium", "unittest")
def test01(self,value):
driver =self.driver
driver.find_element_by_id("kw").clear()
driver.find_element_by_id("kw").send_keys(value)
driver.find_element_by_id("su").click()
time.sleep(4)
@data(['python','python_百度搜索'],["selenium","selenium_百度搜索"],["unittest","unittest_百度搜索"])
@unpack
def test02(self,value,expected_value):
driver =self.driver
driver.find_element_by_id("kw").clear()
driver.find_element_by_id("kw").send_keys(value)
driver.find_element_by_id("su").click()
time.sleep(4)
self.assertEqual(driver.title,expected_value)
@file_data("test_baidu_data.json")
def test03(self,value):
driver =self.driver
driver.find_element_by_id("kw").clear()
driver.find_element_by_id("kw").send_keys(value)
driver.find_element_by_id("su").click()
time.sleep(4)
if __name__ == '__main__':
unittest.main()
共执行了9次测试用例,结果如下
|