通过unittest创建测试
1必须继承与unittest.TestCase类
2可以定义setUp和tearDown方法,也可以定义setUpClass、tearDownClass的类方法
3所有的测试方法必须以test开头。测试方法会在运行时自动被调用
4可用pycharm自带的unittest框架运行,也可以以普通方式运行
Python复制代码
import unittest
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
def testFunc(self):
print("这是一个测试方法")
def testLogin(self):
print("这是一个测试登录的方法")
if __name__ == '__main__':
print("普通的测试入口")
unittest.main()
运行方式的设置
●pycharm自带的unittest框架测试
●从main方法的入口开启的unittest测试
Python复制代码
if __name__ == '__main__':
print("普通的测试入口")
# main方法的入口开启测试
unittest.main()
Run/DebugConfigurations
Name:
Allowparallelrun
unittest-demo-01
Storeasprojectfile
mAN方法作为入口开启测试的配置
Python
Configuration
Logs
时STPythondocs
E:python-studylautotestunittest-demopy
Docutilstask
scriptpath.:
ASTSphinxtask
Parameters
Pythontests
Autodetect
Environment
-demo.py
Doctests
st-demo.D
PYTHONUNBUFFERED-1
Environmentvariables
Nosetests
pytest
不走main方法,使用自带的unittest进行测试
Twisted
Unittests
lnterpreteroptions
Shellscript
的配置,默认是这种配置
SpaceTask
cipython-studyautotest
tOx
AddcontentrootstoPYTHONPATH
AddsourcerootstoPYTHONPATH
Execution
Emulateterminalinoutputconsole
RunwithPythonConsole
Redirectinputfrom:
Editconfigurationtemplates..
OK
Apply
Cancel
pycharm自带的unittest框架测试
这种运行测试,main方法就不会生效。
上面的代码输出结果为:
Plain Text复制代码
setUpClass方法只运行一次 setUp在每个test方法执行之前执行 这是一个测试方法 tearDown在每个test方法执行之后执行 setUp在每个test方法执行之前执行 这是一个测试登录的方法 tearDown在每个test方法执行之后执行 tearDownClass方法只运行一次 .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
设置从main方法的入口开启unittest测试
上面的输出结果为,这个就会走if __name__ == '__main__':中的代码
Plain Text复制代码
普通的测试入口 setUpClass方法只运行一次 setUp在每个test方法执行之前执行 这是一个测试方法 tearDown在每个test方法执行之后执行 setUp在每个test方法执行之前执行 这是一个测试登录的方法 tearDown在每个test方法执行之后执行 tearDownClass方法只运行一次 .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
断言
断言一旦失败了,后面的代码就不会执行
常用的断言有:
●self.assertEqual()
●self.assertNotEqual()
●self.assertIn()
●self.assertNotIn()
●self.assertTrue()
●self.assertFalse()
断言的结果表示
●点.代表断言正确
●F代表断言失败
●E代表代码有错误
实现断言失败后再截图功能
●通过异常处理的方式实现
●通过装饰器的方式实现
通过异常处理的方式
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
from util import Util
@ddt
class Demo(unittest.TestCase):
# 其他方法省略
def test_demo1(self):
try:
self.assertEqual(1, 2)
except AssertionError as e:
import os, time
day = time.strftime('%Y%m%d', time.localtime(time.time()))
sceenshot_path = os.getcwd() + r'reportsscreenshot_%s' % day
if not os.path.exists(sceenshot_path):
os.makedirs(sceenshot_path)
tm = time.strftime('%H%M%S', time.localtime(time.time()))
print(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
self.driver.get_screenshot_as_file(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
# 将异常继续抛出给unittest
raise e
通过装饰器的方式
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
from util import Util
@ddt
class Demo(unittest.TestCase):
# 省略一些方法
def addpic(func):
def wrapper(self, *args, **kwargs):
try:
func(self, *args, **kwargs)
except AssertionError as e:
import os, time
day = time.strftime('%Y%m%d', time.localtime(time.time()))
sceenshot_path = os.getcwd() + r'reportsscreenshot_%s' % day
if not os.path.exists(sceenshot_path):
os.makedirs(sceenshot_path)
tm = time.strftime('%H%M%S', time.localtime(time.time()))
print(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
self.driver.get_screenshot_as_file(sceenshot_path + '\{}_{}.png'.format('screen_shot', tm))
# 将异常继续抛出给unittest
raise e
return wrapper
@addpic
def test_demo1(self):
self.assertEqual(1, 2)
if __name__ == '__main__':
print("普通的测试入口")
TestLoader
自动化用例管理 TestLoader类的常见方法:
●loadTestFromTestCase():从一个python类中加载
●loadTestFromTestModule(): 从一个python文件中加载
●loadTestFromTestNames():从方法名中加载
●discover:从一个路径中加载
loadTestsFromTestCase
Python复制代码
from stringtest import StringTest
from numbertest import NumberTest
import unittest
if __name__ == '__main__':
st = unittest.defaultTestLoader.loadTestsFromTestCase(StringTest)
nt = unittest.defaultTestLoader.loadTestsFromTestCase(NumberTest)
# 将TestCase装到TestSuite中
suite = unittest.TestSuite([st,nt])
# 使用TestRunner运行TestSuite中的所有用例
unittest.TextTestRunner().run(suite)
loadTestsFromModule
Python复制代码
import unittest
import stringtest
import numbertest
if __name__ == '__main__':
names = [numbertest, stringtest]
modules = []
for item in names:
module = unittest.defaultTestLoader.loadTestsFromModule(item)
modules.append(module)
suite = unittest.TestSuite(modules)
unittest.TextTestRunner().run(suite)
loadTestFromTestNames
Python复制代码
from stringtest import StringTest
import unittest
def getFullTestCaseName():
pass
if __name__ == '__main__':
names = ['stringtest.StringTest.test1', 'autotest.numbertest.NumberTest.test2']
smoke_test = unittest.defaultTestLoader.loadTestsFromNames(names)
suite = unittest.TestSuite([smoke_test])
unittest.TextTestRunner().run(suite)
获取一个类中的所有用例getTestCaseNames
Python复制代码
# 获取一个类中的所有的用例 cases = unittest.defaultTestLoader.getTestCaseNames(StringTest) print(cases)
discover
Python复制代码
case_dir = 'autotest' # smoke代表冒烟测试的文件夹,加载冒烟测试文件夹下的所有测试用例 dis = unittest.defaultTestLoader.discover(case_dir + '/smoke', pattern='*.py') suite = unittest.TestSuite(dis) unittest.TextTestRunner().run(suite)
生成测试报告
使用HTMLTestRunner生成测试报告
1配置方法(将HTMLTestRunner_cn.py放到python的安装目录的site-packages目录下)
2生成测试报告
3在报告中添加截图图片
4失败重试功能
Python复制代码
from HTMLTestRunner import HTMLTestRunner
# 保存图片,写在需要保存图片的用例中
self.imgs.append(self.driver.get_screenshot_as_base64())
# 组装TestSuite
st = unittest.defaultTestLoader.loadTestsFromTestCase(StringTest)
nt = unittest.defaultTestLoader.loadTestsFromTestCase(NumberTest)
suite = unittest.TestSuite([st, nt])
runner = HTMLTestRunner(title='测试报告1', description='第一次生成测试报告',
stream=open('test-report.html', 'wb'), verbosity=2)
# 有的版本还可携带retry=2, save_last_try=False参数。这里我下载的HTMLTestRunner版本中没有这两个参数
# retry代表断言失败重试的最大次数,中间如果成功,就不再重试。
# save_last_try代表是否只保留最后一次重试的记录,True表示只保留一次,False表示保留每一次重试的记录
runner.run(suite)
参数化驱动测试用例
所谓参数化,是指利用不同的测试数据来测试相同的场景,为了提高代码的重用性,增加代码效率而采用一种代码编写的方法,叫参数化,也就是数据驱动。达到测试数据和测试业务相分离的效果。
换句话说,就是通过传参的不同,一个方法可以运行多个测试用例,减少代码的冗余,提高代码的复用率。
普通的循环
使用循环来在一个方法中跑多个测试用例,虽然能减少代码的冗余,但实际上在生成的测试报告中,算一个用例,这是不好的。
Python复制代码
import time
import unittest
from selenium import webdriver
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
self.driver = webdriver.Chrome
self.url = '某个url地址'
self.testdata = [
('', 'admin123', 'user id 不能为空'),
('admin', '', 'password不能为空'),
('adminx', 'admin123', '没有此用户'),
('admin', 'xxxx', '密码错误')
]
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
def testFunc(self):
print("这是一个测试方法")
self.assertTrue(False)
def testLogin(self):
print("这是一个测试登录的方法")
self.driver.get(self.url)
time.sleep(1)
for data in self.testdata:
time.sleep(1)
self.driver.find_element_by_id('username').clear()
self.driver.find_element_by_id('username').send_keys(data[0])
time.sleep(1)
self.driver.find_element_by_id('password').clear()
self.driver.find_element_by_id('password').send_keys(data[1])
time.sleep(1)
self.driver.find_element_by_xpath('//input[@value="Login"]').click()
time.sleep(1)
errmsg = self.driver.find_element_by_xpath('//div[contains(text(),"{}")]'.format(data[2])).text
self.assertEqual(errmsg, data[2])
self.driver.find_element_by_xpath('//span[contains(text(),"确定")]').click()
if __name__ == '__main__':
print("普通的测试入口")
unittest.main()
在unittest框架中使用参数化
1安装ddt: pip install ddt
2导入相关的模块:from ddt import ddt,data,unpack
3参数化的几种形式
a参数值为单个的参数形式
b参数值为组合参数形式
c从函数中返回参数值
d从文件中返回参数值
参数值为单个的参数形式
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
# 这个注解是必须的
@ddt
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
# 单个注解的传参形式
@data(1, 2, 3)
def test_data(self, value):
print(value)
参数值为多个的形式
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
@ddt
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
self.driver = webdriver.Chrome
self.url = '某个url地址'
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
@data(
('', 'admin123', 'user id 不能为空'),
('admin', '', 'password不能为空'),
('adminx', 'admin123', '没有此用户'),
('admin', 'xxxx', '密码错误')
)
# 表示将元组的每一行作为一条数据
@unpack
def testLogin2(self, username, password, err):
print("username={},password={},err={}".format(username, password, err))
@data(
('', 'admin123', 'user id 不能为空'),
('admin', '', 'password不能为空'),
('adminx', 'admin123', '没有此用户'),
('admin', 'xxxx', '密码错误')
)
@unpack
def testLogin(self, username, password, err):
print("这是一个测试登录的方法")
self.driver.get(self.url)
time.sleep(1)
self.driver.find_element_by_id('username').clear()
self.driver.find_element_by_id('username').send_keys(username)
time.sleep(1)
self.driver.find_element_by_id('password').clear()
self.driver.find_element_by_id('password').send_keys(password)
time.sleep(1)
self.driver.find_element_by_xpath('//input[@value="Login"]').click()
time.sleep(1)
errmsg = self.driver.find_element_by_xpath('//div[contains(text(),"{}")]'.format(err)).text
self.assertEqual(errmsg, err)
self.driver.find_element_by_xpath('//span[contains(text(),"确定")]').click()
if __name__ == '__main__':
print("普通的测试入口")
unittest.main()
从函数中传来参数值
定义外部函数:
Python复制代码
class Util:
@classmethod
def get_data(cls):
testdata = [
('', 'admin123', 'user id 不能为空'),
('admin', '', 'password不能为空'),
('adminx', 'admin123', '没有此用户'),
('admin', 'xxxx', '密码错误')
]
return testdata
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
# 导入外部函数所在的类
from util import Util
@ddt
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
self.driver = webdriver.Chrome
self.url = '某个url地址'
self.testdata = [
('', 'admin123', 'user id 不能为空'),
('admin', '', 'password不能为空'),
('adminx', 'admin123', '没有此用户'),
('admin', 'xxxx', '密码错误')
]
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
#
# @data(1, 2, 3)
# def test_data(self, value):
# print(value)
@data(*Util.get_data())
@unpack
def testLogin2(self, username, password, err):
print("username={},password={},err={}".format(username, password, err))
if __name__ == '__main__':
print("普通的测试入口")
unittest.main()
从文件中传来参数值
data.txt文件
Python复制代码
, admin123, user id 不能为空 admin, , password不能为空 adminx, admin123, 没有此用户 admin, xxxx, 密码错误
Python复制代码
class Util:
@classmethod
def get_data_from_file(cls, path):
rows = []
with open(path, 'r', encoding='utf-8') as f:
for line in f:
user_data = line.strip().split(',')
rows.append(user_data)
return rows
Python复制代码
import time
import unittest
from selenium import webdriver
from ddt import ddt, data, unpack
from util import Util
@ddt
class Demo(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("setUpClass方法只运行一次")
@classmethod
def tearDownClass(cls):
print("tearDownClass方法只运行一次")
def setUp(self):
print("setUp在每个test方法执行之前执行")
self.driver = webdriver.Chrome
self.url = '某个url地址'
def tearDown(self):
print("tearDown在每个test方法执行之后执行")
@data(*Util.get_data_from_file('data.txt'))
# @data(*Util.get_data())
@unpack
def testLogin2(self, username, password, err):
print("username={},password={},err={}".format(username, password, err))
if __name__ == '__main__':
print("普通的测试入口")
unittest.main()
如果对python自动化测试、web自动化、接口自动化、移动端自动化、大型互联网架构技术、面试经验交流等等感兴趣的老铁们,可以关注我。我会在公众号(程序员阿沐)/群里(810119819)不定期的发放免费的资料链接,这些资料都是从各个技术网站搜集、整理出来的,如果你有好的学习资料可以私聊发我,我会注明出处之后分享给大家。欢迎分享,欢迎评论,欢迎转发。需要资料的同学可以关注我获取资料链接。



