Python 2.1及以后的版本,将unittest作为一个标准模块放入Python开发包中。
01 使用unittest编写测试用例
规则:
- import unittest
- 创建一个测试类,必须要继承unittest.TestCase类
- 创建一个测试方法,且方法要以“test” 开头
from calculator import Calculator
import unittest
class TestAdd(unittest.TestCase):
def test_add(self):
c =Calculator(3,5)
result = c.add()
self.assertEqual(result,8)
if __name__ =='__main__':
unittest.main() //通过main()方法来执行测试用例;按照测试类、方法的名称ASCII值大小的顺序执行用例
unittest的执行结果:
- “.” 表示测试用例执行通过
- “F”表示执行失败
- “E” 表示执行错误
- "s“表示运行跳过
02 三个重要概念
-
Test Case
最小的测试单元,即测试方法。
unittest提供了TestCase基类,我们创建的测试类要继承该基类,它可以用来创建新的测试用例。
-
Test Suite
测试用例、测试套件或两者的集合,用于组装一组要运行的测试。
使用TestSuite类来创建测试套件。
-
Test Runner
Test Runner是一个组件,用于协调测试的执行并向用户提供结果。
unittest提供了TextTestRunner类运行测试用例。
03 测试用例执行顺序
unittest默认按照ASCII码的顺序加载测试用例(包括测试目录和测试文件、测试类、测试方法),即它并不是按照测试用例的创建顺序从上到下执行的。
discover() 和 main()方法的执行顺序是一样的。故想让某个测试文件先执行,可以在命名上加以控制。
如何控制测试用例的执行顺序?
可以通过TestSuite类的addTest()方法按照一定的顺序来加载测试用例,这样想先被执行的用例就可以先加载。
from calculator import Calculator
import unittest
class TestAdd(unittest.TestCase):
def test_add(self):
c =Calculator(3,5)
result = c.add()
self.assertEqual(result,8)
def test_add_decimals(self):
c=Calculator(3.5,5.5)
result=c.add()
self.assertEqual(result,9)
class TestSub(unittest.TestCase):
def test_sub(self):
c =Calculator(5,1)
result = c.sub()
self.assertEqual(result,4)
if __name__ =='__main__':
#创建测试套件
suit = unittest.TestSuite()
suit.addTest(TestSub("test_sub")) //添加测试用例
suit.addTest(TestAdd("test_add_decimals"))
#创建测试运行器
runner = unittest.TextTestRunner()
runner.run(suit)
04 执行多个测试用例
unittest.defaultTestLoader.discover()方法可以从多个文件中查找测试用例。
该类根据各种标准加载测试用例,并将它们返回给测试套件
discover(start_dir, pattern='Test*.py', top_level_dir=None)
-
start_dir:待测试的模块名/测试用例目录;
discover()方法会自动根据这个参数查找测试用例文件
-
pattern:测试用例文件名的匹配原则
-
top_level_dir:测试模块的*目录,如果没有*目录,默认为None
import unittest
test_dir='Test' //文件目录
suits =unittest.defaultTestLoader.discover(test_dir, pattern='Test*.py')
if __name__=='__main__':
runner = unittest.TextTestRunner()
runner.run(suits)
如果想让discover()查找子目录下的测试文件,得将子目录标记为一个python模块(子目录下放__init__.py文件)
05 跳过测试和预期失败
import unittest
class MyTest(unittest.TestCase):
@unittest.skip("跳过这条用例")
def test_skip(self):
print('aaa')
@unittest.skipIf(3>2,"当条件为真时跳过测试")
def test_skip_if(self):
print('bbb')
@unittest.skipUnless(3>2,"当条件为假时跳过测试")
def test_skip_unless(self):
print('ccc')
// 不论执行结果是什么,都将测试标记为失败
@unittest.expectedFailure
def test_fail(self):
print('ddd')
if __name__=="__main__":
unittest.main()
执行结果:
E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/skip_fail.py
ddd
ussccc
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (skipped=2, unexpected successes=1)
以上四个装饰器同样适用于测试类。
06 Setup和Teardown
-
setUpModule/tearDownModule
在整个模块的开始与结束时被执行
-
setUpClass/ tearDownClass
在测试类的开始与结束时被执行
-
setUp/tearDown
在测试用例的开始与结束时被执行
import unittest
def setUpModule():
print(" Module start .....")
def tearDownModule():
print(" Module end ...")
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Class Start...")
@classmethod
def tearDownClass(cls):
print("Class end...")
def setUp(self):
print("test case start ...")
def tearDown(self):
print("test case end ...")
def testcase1(self):
print("this is first test case")
def testcase2(self):
print("this is second test case")
if __name__ == "__main__":
unittest.main()
执行结果如下:
E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/setup.py
Module start .....
Class Start...
test case start ...
this is first test case
test case end ...
.test case start ...
this is second test case
test case end ...
.Class end...
Module end ...
----------------------------------------------------------------------
Ran 2 tests in 0.002s
OK
07 Web自动化测试
import unittest
from selenium import webdriver
from time import sleep
class Baidu(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.driver.implicitly_wait(5)
cls.driver.get("http://www.baidu.com")
cls.driver.maximize_window()
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def search(self,text):
self.driver.find_element_by_id("kw").clear()
self.driver.find_element_by_id("kw").send_keys(text)
self.driver.find_element_by_id("su").click()
sleep(3)
def test_search_selenium(self):
search_key="selenium"
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
def test_search_python(self):
search_key="python"
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
if __name__ =="__main__":
unittest.main()
08 Parameterized
Parameterized是python的一个参数化库,同时支持unittest、pytest单元测试框架。
- pip install parameterized
- parameterized.expand() 加载数据,列表中每个元组是一条测试用例
import unittest
from time import sleep
from selenium import webdriver
from parameterized import parameterized
class TestBaidu(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver=webdriver.Chrome()
cls.driver.get("http://www.baidu.com")
cls.driver.maximize_window
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def search(self,search_key):
self.driver.find_element_by_id("kw").clear()
self.driver.find_element_by_id("kw").send_keys(search_key)
self.driver.find_element_by_id("su").click()
sleep(5)
@parameterized.expand(
[
("case1","selenium"),
("case2","python")
]
)
def test_search(self,name,search_key):
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
if __name__ == '__main__':
unittest.main(verbosity=2)
09 DDT
Data-Driven Tests 是针对unittest单元测试框架设计的扩展库。
安装:
pip install ddt
导入:
from ddt import ddt, data, file_data,unpack
使用规则:
- 必须用 @ddt 装饰测试类
- 数据有不同形式的参数化,如下方的元组、列表、字典类型
- @data( 数据列表/元组/字典,字典的key和方法中的形参名称保持一致)
- @unpack 装饰测试方法
import unittest
from time import sleep
from selenium import webdriver
from ddt import ddt,data,file_data,unpack
@ddt
class TestBaidu(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver=webdriver.Chrome()
cls.driver.get("http://www.baidu.com")
cls.driver.maximize_window
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def search(self,search_key):
self.driver.find_element_by_id("kw").clear()
self.driver.find_element_by_id("kw").send_keys(search_key)
self.driver.find_element_by_id("su").click()
sleep(5)
@data(
("case1","selenium"),
("case2","python")
)
@unpack
def test_search1(self,name,search_key):
print("第一组测试用例:",name)
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
@data(
["case1","selenium"],
["case2","python"]
)
@unpack
def test_search2(self,name,search_key):
print("第二组测试用例:",name)
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
@data(
{"key":"selenium"},
{"key":"python"}
)
@unpack
def test_search3(self,key):
print("第三组测试用例:",key)
self.search(key)
self.assertEqual(self.driver.title,key+"_百度搜索")
if __name__ == '__main__':
unittest.main(verbosity=2)
执行报错如下时,是因为文件名也为ddt:
PS E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/ddt.py
Traceback (most recent call last):
File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
from ddt import ddt,data,file_data,unpack
File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
from ddt import ddt,data,file_data,unpack
ImportError: cannot import name 'ddt' from partially initialized module
'ddt' (most likely due to a circular import)
(e:\selenium\Test\unittest\ddt.py)
10 数据文件的参数化
@file_data()装饰器中内容为文件名称。支持json格式和yaml格式。
import unittest
from time import sleep
from selenium import webdriver
from ddt import ddt,data,file_data,unpack
@ddt
class TestBaidu(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver=webdriver.Chrome()
cls.driver.get("http://www.baidu.com")
cls.driver.maximize_window
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def search(self,search_key):
self.driver.find_element_by_id("kw").clear()
self.driver.find_element_by_id("kw").send_keys(search_key)
self.driver.find_element_by_id("su").click()
sleep(5)
@file_data('ddt_data.json')
@unpack
def test_search1(self,search_key):
print("第一组测试用例:",search_key)
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
@file_data('ddt_data_yml.yaml')
@unpack
def test_search2(self,case):
search_key=case[0]["search_key"]
print("第二组测试用例:",search_key)
self.search(search_key)
self.assertEqual(self.driver.title,search_key+"_百度搜索")
if __name__ == '__main__':
unittest.main(verbosity=2)
ddt_data.json
{
"case1": {"search_key": "python"},
"case2": {"search_key": "ddt"}
}
ddt_data_yml.yaml
case1:
- search_key: "python"
case2:
- search_key: "ddt"
技术行业要不断地学习,学习肯定不要孤军奋战,最好是能抱团取暖,相互成就一起成长,群众效应的效果是非常强大的,大家一起学习,一起打卡,会更有学习动力,也更能坚持下去。你可以加入我们的测试技术交流扣扣群:914172719(里面有各种软件测试资源和技术讨论)
送给大家一句话,共勉:当我们能力不足的时候,首先要做的是内修!当我们能力足够强大的时候,就可以外寻了!
最后也为大家准备了一份配套的学习资源,你能在 公众号:【伤心的辣条】免费获取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中资料包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。
好文推荐
转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!
面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…
4个月自学软件测试面进阿里!如何从功能测试转成自动化…我经历了什么
6000元报了培训班,3个月后我成功“骗”进了腾讯大厂,月薪15000