前面讲了如何不写代码可以实现自动化。 但是录制的代码很杂乱且冗余,可读性和维护性太差。 下面来介绍如何搭建自动化框架,将录制的代码整理,让其变得简洁。 python市面见得多的是以下两种框架。 1.unittest
(1)测试类必须继承unittest.TestCase
(2)测试函数必须以”test_”开头
(3)测试类必须有unittest.main()方法
2.pytest
(1)测试文件的文件名必须以”test_”开头,或者以”_test”结尾
(2)测试类命名必须以”Test”开头
(3)测试函数名必须以”test”开头
(4)测试类里面不能使用”init”方法
总结:pytest是基于unittest衍生出来的新的测试框架,使用起来相对于unittest来说更简单、效率来说更高,pytest兼容unittest测试用例,但是反过来unittest不兼容pytest,所以说pytest的容错性更好一些!在使用交互逻辑上面pytest比unittest更全一些!
unittest是python自带的测试库,自我感觉的话,如果对python只是了解基础知识,学unittest框架相对于来说要好理解些,unittest框架也完全可以实现市场上大部分的业务测试!建议可以先了解下unittest框架,然后在学pytest框架,可以更好的去感受一下两个框架的优缺点。
unittest是python内置的用于测试代码的模块,无需安装, 使用简单方便 unittest工作原理 写好一个完整的TestCase 多个TestCase 由TestLoder被加载到TestSuite里面, TestSuite也可以嵌套TestSuite 由TextTestRunner来执行TestSuite,测试的结果保存在TextTestResult中 TestFixture指的是环境准备和恢复
unittest中最核心的部分是:TestFixture、TestCase、TestSuite、TestRunner
Test Fixture
用于测试环境的准备和恢复还原, 一般用到下面几个函数。
- setUp():准备环境,执行每个测试用例的前置条件
- tearDown():环境还原,执行每个测试用例的后置条件
- setUpClass():必须使用@classmethod装饰器,所有case执行的前置条件,只运行一次
- tearDownClass():必须使用@classmethod装饰器,所有case运行完后只运行一次
Test Case
- 参数verbosity可以控制错误报告的详细程度:
默认为1
。0,表示不输出每一个用例的执行结果;2表示详细的执行报告结果。 - Verbosity=1情况下
成功是 .
,失败是 F,出错是 E,跳过是 S - 测试的执行跟方法的顺序没有关系, 默认按字母顺序
- 每个测试方法均
以 test 开头
- Verbosity=2情况下会打印测试的注释
被测代码,demo.py文件
代码语言:javascript复制#!/usr/bin/python
# -*- coding: utf-8 -*-
def add(a, b):
return a b
def minus(a, b):
return a-b
测试case, test_demo_class.py文件
代码语言:javascript复制#!/usr/bin/python
# -*- coding: utf-8 -*-
import unittest
from demo import add, minus
class TestDemo(unittest.TestCase):
"""Test mathfuc.py"""
@classmethod
def setUpClass(cls):
print ("this setupclass() method only called once.n")
@classmethod
def tearDownClass(cls):
print ("this teardownclass() method only called once too.n")
def setUp(self):
print ("do something before test : prepare environment.n")
def tearDown(self):
print ("do something after test : clean up.n")
def test_add(self):
"""Test method add(a, b)"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
self.assertNotEqual(1, minus(3, 2))
@unittest.skip("do't run as not ready")
def test_minus_with_skip(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
self.assertNotEqual(1, minus(3, 2))
if __name__ == '__main__':
# verbosity=*:默认是1;设为0,则不输出每一个用例的执行结果;2-输出详细的执行结果
unittest.main(verbosity=1)
Test Suite
- 一般通过
addTest()
或者addTests()
向suite中添加。case的执行顺序与添加到Suite中的顺序是一致的 - @unittest.skip()装饰器跳过某个case (1)skip():无条件跳过 @unittest.skip("i don't want to run this case. ") (2)skipIf(condition,reason):如果condition为true,则 skip @unittest.skipIf(condition,reason) (3)skipUnless(condition,reason):如果condition为False,则skip @unittest.skipUnless(condition,reason)
Test Loder
TestLoadder
用来加载TestCase到TestSuite中。loadTestsFrom*()
方法从各个地方寻找testcase,创建实例,然后addTestSuite,再返回一个TestSuite实例- defaultTestLoader() 与 TestLoader()功能差不多,复用原有实例
unittest.TestLoader().loadTestsFromTestCase(testCaseClass) unittest.TestLoader().loadTestsFromModule(module) unittest.TestLoader().loadTestsFromName(name,module=None) unittest.TestLoader().loadTestsFromNames(names,module=None) unittest.TestLoader().discover()
实例如下, test_demo_module.py文件
代码语言:javascript复制#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import HTMLReport
import unittest
import test_demo_class
from test_demo_class import TestDemo
if __name__ == '__main__':
paras = sys.argv[1:]
args = paras[0]
report = paras[1]
suite = unittest.TestSuite()
if args == 'test':
tests = [TestDemo("test_minus"), TestDemo("test_add"), TestDemo("test_minus_with_skip")]
suite.addTests(tests)
elif args == 'tests':
suite.addTest(TestDemo("test_minus"))
suite.addTest(TestDemo("test_add"))
suite.addTest(TestDemo("test_minus_with_skip"))
elif args == 'class':
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestDemo))
elif args == 'module':
suite.addTests(unittest.TestLoader().loadTestsFromModule(test_demo_class))
elif args == 'mix':
suite.addTests(unittest.TestLoader().loadTestsFromName('test_demo_class.TestDemo.test_minus'))
elif args == 'mixs':
suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_demo_class.TestDemo.test_minus', 'test_demo_class.TestDemo', 'test_demo_class']))
elif args == 'discover':
suite.addTests(unittest.TestLoader().discover('.', 'test_*.py', top_level_dir=None))
if report == 'terminal':
runner = unittest.TextTestRunner(verbosity=1)
runner.run(suite)
elif report == 'txt':
with open('ut_log.txt', 'a') as fp:
runner = unittest.TextTestRunner(stream=fp, verbosity=1)
runner.run(suite)
elif report == 'html':
runner = HTMLReport.TestRunner(report_file_name='test',
output_path='report',
title='测试报告',
description='测试描述',
sequential_execution=True
)
runner.run(suite)
Testing Report
- 终端报告:如上
terminal
分支 - TXT报告:如上
txt
分支,当前目录会生成ut_log.txt文件 - HTML 报告:如上
html
分支,终端上打印运行信息
同时会在当前目录生成report文件夹, 文件夹下有test.html
和test.log
文件
例子如下: 创建一个函数集mathfunc.py
代码语言:javascript复制def add(a,b):
return a b
def minus(a, b):
return a-b
def multi(a, b):
return a*b
def divide(a, b):
return a/b
接下来是为这些方法写的一个测试: test_mathfunc.py
代码语言:javascript复制import unittest
from mathfunc import *
class TestMathFunc(unittest.TestCase):
def setUp(self):
print "do something befor test.prepare environment"
def tearDown(self):
print "do something after test.Clean up"
def test_add(self):
self.assertEqual(3,add(1,2))
self.assertNotEqual(3,add(2,2))
@unittest.skip("i don't want to run this case")
def test_minus(self):
self.assertEqual(1,minus(3,2))
def test_multi(self):
self.assertEqual(6,multi(2,3))
def test_divide(self):
self.assertEqual(2,divide(6,3))
self.assertEqual(2.5,divide(5,2))
if __name__ == '__main__':
unittest.main()
组织TestSuite
上面的代码演示了如何编写一个简单的测试,下面说一下怎么控制用例执行的顺序。我们就要用到TestCase,添加到TestCaseDE中的case是会按照添加的顺序执行的。
来个例子:
在文件夹中再新建一个文件。test_suite.py
代码语言:javascript复制# -*- coding: utf-8 -*-
import unittest
from test_mathfunc import TestMathFunc
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
如何控制用例执行顺序
在unittest中,用例是以test开头的方法定义的,默认执行顺序是根据用例名称升序进行,而不是用例定义的先后顺序。 在unittest中解决用例执行顺序的问题是使用TestSuite来定义顺序
如何让多个用例共用setup、teardown
unittest的setup、teardown会在每个用例执行前后执行一次,如上面测试用例类中有3个测试用例, 那么每个用例执行前会执行setup,执行后会执行teardown,即setup、teardown总共会调用三次, 但考虑实际自动化测试场景,多个用例只需执行一次setup,全部用例执行完成后,执行一次teardown, 针对该种场景,unittest的处理方法是使用setupclass、teardownclass
如何跳过用例
在自动化测试中,经常会遇到挑选用例的情况,在unittest中的解决方法是使用skip装饰器, 其中skip装饰器主要有3种:unittest.skip(reason)、unittest.skipIf(condition,reason)、 unittest.skipUnless(condition,reason),即在满足condition条件下跳过该用例, reason用于描述跳过的原因
如何生成html格式的测试报告
Unittest中默认生成的报告格式为txt,如果想生成html格式的报告,可以使用HtmlTestRunner模块, 安装后导入该模块,使用HTMLTestRunner代替默认的TextTestRunner()执行测试用例即可。
代码语言:javascript复制import unittest
from test_mathfunc import TestMathFunc
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
suite = unittest.TestSuite()
#使用这种方法可以对测试用例排序
#tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]
#suite.addTests(tests)
#使用TestLoader的方法传入TestCase
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
#在同目录下生成txt格式的测试报告
#with open('UnittestTextReport.txt', 'a') as f:
#runner = unittest.TextTestRunner(stream=f, verbosity=2)
#runner.run(suite)
with open('HTMLReport.html','w') as f:
runner = HTMLTestRunner(stream = f,
title = u'测试报告',
description = u'测试用例的执行情况',
verbosity = 2
)
runner.run(suite)
如果我们想执行某个路径下的case:
代码语言:javascript复制 for filename in os.listdir(self.cases):
if filename == "report":
break
else:
os.mkdir(self.cases '/report')
now = time.strftime("%Y-%m-%d_%H_%M_%S")
fp = open("./report/" now "result.html", 'wb')
tests = unittest.defaultTestLoader.discover("./test_case",pattern='*sta.py',top_level_dir=None)
runner = HTMLTestRunner(stream=fp, title=self.title, description=self.des)
runner.run(tests)
fp.close()
队于htmtestrunner这个文件,我们可以去下载:https://www.cnblogs.com/pandaly/p/13212376.html 也可以去https://github.com/huilansame/HTMLTestRunner_PY3
小结:
1、unittest是python自带的单元测试框架,可以用来作为我们自动化测试框架的用例组织执行框架
2、unittest流程:写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。
3、一个class继承unittest.TestCase即是一个TestCase,其中以 test 开头的方法在load时被加载为一个真正的TestCase。
4、verbosity参数可以控制执行结果的输出,0 是简单报告、1 是一般报告、2 是详细报告。
5、可以通过addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。
6、用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例执行前布置环境,以及在用例执行后清理环境
7、我们可以通过skip,skipIf,skipUnless装饰器跳过某个case,或者用TestCase.skipTest方法。
8、参数中加stream,可以将报告输出到文件:可以用TextTestRunner输出txt报告,以及可以用HTMLTestRunner输出html报告.