python模块详解 | unittest(单元测试框架)
目录:
why unittest?
- unittest的四个重要概念
- 加载测试用例的三个方法
- 自动加载测试用例
- 忽略测试和预期失败
- 生成html测试报告
why unittest ?
简介:
Unittest是python自带的单元测试框架,设计灵感来自Java中的Juint,具有和Junit类似的结构
优点:
- 不仅可以用于单元测试,还适用于自动化测试用例的开发与执行
- 可阻止执行测试用例
- 断言预期结果
- 批量执行测试用例
- 最终可生成测试结果
unittest四个重要的概念
1.1 测试用例(TestCase)
-
测试用例是单元测试中最基本的组成单元
-
Unittest使用TestCase类(可自行命名)来表示测试用例,所有执行测试的类继承自该类
-
一个测试用例包括:测试固件、具体测试业务的函数或者方法
-
测试用例中,测试固件可以省略,但是至少有一个以test开头的测试函数testXXX
-
Unittest会自动化识别test开头的函数为测试代码,所有的测试函数都要以test开头(如testXXX,注意test是小写),如果函数不以test开头,则Unittest不会执行函
-
例:
import unittest # 自定义一个测试实例类,传入unittest的测试实例类TestCase class TestBaidu(unittest.TestCase): driver=webdriver.Chrome() driver.get(bd_url) def test_1(self): print(1) # 断言等于 self.assertEqual(self.driver.title,u'百度一下,你就知道') def test_2(self): print(2) # 断言为真 self.assertTrue(self.driver.find_element_by_id('kw').is_enabled()) if __name__ == '__main__': unittest.main()
1.2 测试固件(setUp、tearDown、setUpClass、tearDownClass)
- **setUp() **
- 在每个测试用例运行前运行,该方法用于进行测试前的初始化工作
- 一条用例执行一次,若N次用例就执行N次,根据用例的数量来定
- tearDown()
- 在每个测试用例运行后运行,该方法用于执行测试后的清除工作(不管是否执行成功)
- 例:
import unittest
# 自定义一个测试实例类,传入unittest的测试实例类TestCase
class TestBaidu(unittest.TestCase):
def setUp(self):
print('一个测试用例前的初始化工作...')
self.driver = webdriver.Chrome()
self.driver.get(bd_url)
def tearDown(self):
print('一个测试用例前的清除工作...')
self.driver.quit()
def test_1(self):
print(1)
# 断言等于
self.assertEqual(self.driver.title,u'百度一下,你就知道')
def test_2(self):
print(2)
# 断言为真
self.assertTrue(self.driver.find_element_by_id('kw').is_enabled())
ps:
- 如果setUp()执行成功(没有异常),那么无论测试方法是否通过,tearDown()都会被执行;
- setUp()和tearDown()方法存在弊端,例如每次执行用例是都会重新打开,导致时间浪费,因此可以使用下面两种方法;
-
setUpClass()
- 所有用例执行前只运行一次,在所有用例执行之前准备一次环境
-
tearDownClass()
- 所有用例执行完后只运行一次,在所有用例执行结束后再清理环境
-
例:
import unittest # 自定义一个测试实例类,传入unittest的测试实例类TestCase class TestBaidu(unittest.TestCase): @classmethod def setUpClass(cls) -> None: print('所有测试用例前的准备工作...') cls.driver=webdriver.Chrome() cls.driver.get(bd_url) @classmethod def tearDownClass(cls) -> None: print('所有测试用例执行后的清理工作...') cls.driver.quit() def setUp(self): print('一个测试用例前的初始化工作...') self.driver = webdriver.Chrome() self.driver.get(bd_url) def tearDown(self): print('一个测试用例前的清除工作...') self.driver.quit() def test_1(self): print(1) # 断言等于 self.assertEqual(self.driver.title,u'百度一下,你就知道') def test_2(self): print(2) # 断言为真 self.assertTrue(self.driver.find_element_by_id('kw').is_enabled()) if __name__ == '__main__': unittest.main()
1.3 测试套件(TestSuite)
-
完整的单元测试很少只执行一个测试用例,开发人员通常都需要编写多个测试用例才能对某一软件功能进行完整的测试
-
测试套件是多个测试用例或者测试用例集合聚合组织起来的集合,是针对被测程序对应的功能和模块创建的一组测试,测试套件内的测试用例将一起执行,即批量执行一个测试套件内所有的测试用例
-
在unittest中,测试套件主要通过unittest.TestSuite()类直接构建,或者通过TestSuite实例的addTests、addTest方法构建
-
例:
if __name__ == '__main__': # 测试套件写法1 suite1=unittest.TestSuite(map(TestBaidu,['test_1','test_2'])) # 测试套件写法2 suite2=unittest.TestSuite() suite2.addTests(map(TestBaidu,['test_1','test_2'])) # 测试套件写法3 suite3 = unittest.TestSuite() suite3.addTest(TestBaidu('test_1')) suite3.addTest(TestBaidu('test_2'))
1.4 测试执行器(TextTestRunner)
-
测试执行器是一个组织安排测试脚本执行活动的组件,即用来加载测试用例并执行用例,且提供测试输出的一个组件
-
测试执行器负责测试执行调试并且通过一些图形界面,文本界面或者返回一些特殊的值来展示测试脚本的测试结果给用户
-
测试的执行也是单元测试中非常重要的一个概念,一般单元测试框架中都会提供丰富的执行策略和执行结果
-
测试执行器可以加载测试用例或者测试套件来执行测试任务,即利用猜测是执行器加载的测试用例,可以是单个测试用例,也可以是套件
-
例:
#测试执行器 runner=unittest.TextTestRunner() runner.run(suite)
加载测试用例的三个方法
2.1 unittest.main()
- 两个参数(exit、verbosity):
参数 | 说明 |
---|---|
verbosity | 若verbosity=0,则输出简单报告,获得总的测试用例数和总的结果。比如,总共100个,失败20 成功80; 若verbosity=1,则输出一般报告,每个成功的用例前面有个“.”,每个失败的用例前面有个“F”,默认值为1; 若verbosity=2,则输出详细报告,测试结果会显示每个测试用力的所有相关的信息; 添加参数–quite,等效于verbosity=0; 添加参数–version,等效于verbosity=2; 不增加,等效于verbosity=1; |
exit | exit=False表示中间有用例失败也继续执行 |
-
例:
if __name__ == '__main__': unittest.main()
2.2 测试套件(TestSuite)
-
步骤:
- 创建测试套件
- 构建测试套件
- 使用测试执行器运行测试套件
-
参考:测试套件
2.3 unittest.TestLoader()
- 步骤:
- 加载测试用例:unittest.Loader()
- 添加测试用例到测试套件
- 使用测试执行器(unittest.TextTestRunner())运行测试套件
- 实例化runner类:runner=unittest.TextTestRunner()
- 运行测试:runner.run(suite)
- unittest.TestLoader()加载测试用例的方法:
TestLoader().loadTestsFromTestCase(testCaseClass) | testCaseClass必须是TestCase的子类(或者孙类) |
---|---|
TestLoader().loadTestsFromModule(module,pattern=None) | module是测试用例所在的模块 |
TestLoader().loadTestsFromName(name,module=None) | name是测试用例的方法名,使用格式是“module.class.method” |
TestLoader().loadTestsFromNames(names,module=None) |
- 方法图示:
- 例:
#加载测试用例
test_case=unittest.TestLoader().loadTestsFromTestCase(BaiduTest)
#加载测试套件
suite=unittest.TestSuite()
suite.addTest(test_case)
#执行测试
runner=unittest.TextTestRunner()
runner.run(suite)
自动加载测试用例
简介:
unittest的TestLoader类提供了一个discover方法,以递归的方式,实现从指定顶层目录、模块找到指定目录里的测试用例文件,并将查找到的测试用例文件组装到测试套件中,然后运行。
- discover(start_dir,pattern,top_level_dirt)
参数 | 说明 |
---|---|
start_dir | 被测试的模块或测试用例所在的目录 |
pattern | 用例文件名的匹配原则(默认匹配test*.py) |
top_level_dir | 测试模块的顶层目录,如果没有顶层目录,默认为None |
-
步骤:
- 获取目录下满足条件的所有包含测试用例的py文件,构造测试集
- 实例化测试执行器,运行测试用例
-
例:
#discover()方法加载所有测试用例进行测试
discover = unittest.TestLoader.discover(start_dir='./',
pattern='*.py',
top_level_dir=None)
#实例化测试执行器
runner = unittest.TextTestRunner()
#运行测试用例
runner.run(discover)
忽略测试和预期失败
@unittest.skip(reason) | 无条件跳过被装饰的测试方法,并说明跳过测试的原因;miaosureson:理由,描述为什么跳过测试用例 |
@unittest.skipIf(condition,reason) | 条件为真时,跳过被装饰的测试用例,并说明跳过测试的原因 |
@unittest.skipUnless(condition,reason) | 除非条件为真,否则跳过被装饰的测试用例;即条件为假时,跳过装饰的测试用例,并说明跳过测试的原因 |
@unittest.expectedFailure | 将测试用例标记为“预期失败” |
@unittest.SkipTest(reason) | 当需要跳过一个测试方法时,可以在测试方法或setup()中调用该方法 |
class BaiduTest2(unittest.TestCase):
def setUp(self) -> None:
print('\n测试开始...')
self.driver=webdriver.Firefox()
self.driver.get(bd_url)
def tearDown(self) -> None:
self.driver.quit()
print('测试结束...')
@unittest.skip('跳过此测试,没有为什么')
def test1(self):
self.driver.find_element_by_id('kw').send_keys(keywords[0])
self.driver.find_element_by_id('su').click()
time.sleep(1)
print(self.driver.title)
self.assertEqual(self.driver.title,'Python_百度搜索')
@unittest.skipIf(condition=1<2,reason='又跳过了,因为2大于1')
def test2(self):
self.driver.find_element_by_id('kw').send_keys(keywords[1])
self.driver.find_element_by_id('su').click()
time.sleep(1)
print(self.driver.title)
self.assertEqual(self.driver.title,'Selenium_百度搜索')
@unittest.skipUnless(condition=1<2,reason='2大于1,执行此测试')
def test3(self):
self.driver.find_element_by_id('kw').send_keys(keywords[2])
self.driver.find_element_by_id('su').click()
time.sleep(1)
print(self.driver.title)
self.assertEqual(self.driver.title,'TestSuite_百度搜索')
html测试报告
5.1 配置HTMLTestRunner
-
工具:HTMLTestRunner
-
安装:
Windows:将HTMLTestRunner.py文件移动到python安装目录下的lib文件夹内
Linux:将文件移动到安装目录下的第三方包文件夹(dist-packages)目录下
-
修改HTMLTestRunner.py文件,原因:由于下载的文件是基于python2语法,若使用的是python3,则需要修改
行数 | 修改前 | 修改后 |
---|---|---|
94 | import StringIO |
import io |
539 | self.outputBuffer = StringIO.StringIO() |
self.outputBuffer = io.StringIO() |
631 | print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) |
print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)) |
642 | if not rmap.has_key(cls): |
if not cls in rmap: |
766 | uo = o.decode('latin-1') |
uo = e |
768 | uo = o |
uo = o.decode('utf-8') |
772 | ue = e.decode('latin-1') |
ue = e |
774 | ue = e |
ue = e.decode('utf-8') |
5.2 生成测试报告
配置htmltestrunner成功后,在测试代码中导入HTMLTestRunner模块,并在测试文件中加入下述代码即可