unittest 是python 的单元测试框架, 在python 的官方文档中,对unittest有详细的介绍,可以到https://www.python.org/doc/ 去了解。
unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用。
test fixture:初始化和清理测试环境,比如创建临时的数据库,文件和目录等,其中 ==setUp() 和 setDown()==是最常用的方法
test case:单元测试用例,TestCase 是编写单元测试用例最常用的类
test suite:单元测试用例的集合,TestSuite 是最常用的类
test runner:执行单元测试
test report:生成测试报告
1.测试固件(框架里面的固定的方法)
setup方法,测试环境和数据的准备工作
teardown() : 环境的清理工作
2.测试用例
一个用例是一个方法 def名字以test_开头
所有测试类中自定义方法,都必须以test_开头
运行脚本的时候默认自动运行test_ 开头的方法。
unittest的书写规则?
1.测试类必须继承unittest.TestCase类
2.测试类也必须以Test开头,如:calss TestLogin(unittest.TestCase)
3.在测试的py文件里面去执行代码,unittest.main()
4.在测试的类里面可以有普通方法,但是普通方法需要被测试方法调用
3.测试套件
把测试用例组织到一起进行一个整体的测试
# 定义测试套件
testunit = unittest.TestSuite()
# 向测试套件添加测试用例
testunit.addTest(TestBaiDu("test_search_set"))
testunit.addTest(TestBaiDu("test_baidu_search"))
# 把一个类里面所有测试用例添加进去 unittest.makeSuite(testbaidu1.Baidu1)
# (脚本名.类名)
testunit.addTest(unittest.makeSuite(test08.TestBaiDu))
# 把一个类中所有测试方法创建测试套件返回
suite = unittest.TestSuite()
suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)
suite = unittest.TestSuite([suite1, suite2])
# 把一个文件夹下所有测试脚本的测试用例执行一遍
discover=unittest.defaultTestLoader.discover('../test',pattern='test*.py',top_level_dir=None)
makeSuite()和TestLoader()的应用
在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。
TestLoader 用于创建类和模块的测试套件,一般的情况下TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。
discover()
discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件.
示例:
1.在百度首页输入关键字搜索
2.百度首页搜索设置
两个测试用例相继执行,并形成测试报告
from selenium import webdriver
from time import sleep
import unittest
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
# 导入运行结果报告生成模块
from HTMLTestRunner import HTMLTestRunner
# 定义测试类,继承unittest.TestCase
class TestBaiDu(unittest.TestCase):
#定义测试类方法
#所有测试类中自定义方法,都必须以test_开头
def test_baidu_search(self):
"""
在百度首页输入关键字搜索
:return:
"""
wd=webdriver.Chrome()
wd.get("http://wwww.baidu.com")
sleep(3)
wd.find_element(By.ID, 'kw').send_keys("刘耀文")
sleep(2)
wd.find_element(By.ID, 'su').click()
sleep(2)
# 设置用例执行的预期结果 判断是否在网页标题中
exp = "刘耀文" in wd.title
# 利用断言方法
self.assertTrue(exp, "标题中没有你想要的关键字")
wd.quit()
pass
def test_search_set(self):
"""
百度首页搜索设置
:return:
"""
wd = webdriver.Chrome()
wd.get("http://wwww.baidu.com")
sleep(3)
aset = wd.find_element(By.ID, 's-usersetting-top')
ActionChains(wd).move_to_element(aset).perform()
sleep(2)
wd.find_element(By.LINK_TEXT, '搜索设置').click()
sleep(2)
wd.find_element(By.LINK_TEXT, '保存设置').click()
# 处理弹窗
sleep(2)
wd.switch_to.alert.accept()
sleep(2)
print(wd.current_url)
# 判断首页网址
self.assertEqual("https://www.baidu.com/", wd.current_url, "返回首页有误")
wd.quit()
pass
# 定义测试套件
testunit = unittest.TestSuite()
# 向测试套件添加测试用例
testunit.addTest(TestBaiDu("test_search_set"))
testunit.addTest(TestBaiDu("test_baidu_search"))
# 创建测试报告 HTML格式的测试执行报告
fp = open("result.html", "wb")
# 创建执行对象
runner = HTMLTestRunner(stream=fp, title="百度测试搜素", description="用例执行情况:")
runner.run(testunit)
fp.close()
unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0~ 9,A~ Z,a~z 。
所以, TestAdd 类会优先于TestBdd 类被发现, test_aaa() 方法会优先于test_ccc() 被执行
addTest()方法按照增加顺序来执行
对于不想运行的测试用例,方法上加
@unittest.skip(u'The function was canceled, neglects to perform thecase')
自动化的测试中, 对于每个单独的case来说,一个case的执行结果中, 必然会有期望结果与实际结果, 来判断该case是通过还是失败, 在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果。
断言:判断实际结果和预期结果是否相符合。
unittest 的单元测试库提供了标准的xUnit 断言方法:
断言方法 | 断言描述 |
---|---|
assertEqual(arg1, arg2, msg=None) | 验证arg1=arg2,不等则fail |
assertNotEqual(arg1, arg2, msg=None) | 验证arg1 != arg2, 相等则fail |
assertTrue(expr, msg=None) | 验证expr是true,如果为false,则fail |
assertFalse(expr,msg=None) | 验证expr是false,如果为true,则fail |
assertIs(arg1, arg2, msg=None) | 验证arg1、arg2是同一个对象,不是则fail |
assertIsNot(arg1, arg2, msg=None) | 验证arg1、arg2不是同一个对象,是则fail |
assertIsNone(expr, msg=None) | 验证expr是None,不是则fail |
assertIsNotNone(expr, msg=None) | 验证expr是不是None,不是则fail |
assertIn(arg1, arg2, msg=None) | 验证arg1是arg2的子串,不是则fail |
assertNotIn(arg1, arg2, msg=None) | 验证arg1不是arg2的子串,是则fail |
assertIsInstance(obj, cls, msg=None) | 验证obj是cls的实例,不是则fail |
assertNotIsInstance(obj, cls, msg=None) | 验证obj不是cls的实例,是则fail |
本执行完毕之后,还需要看到HTML报告,下面我们就通过HTMLTestRunner.py 来生成测试报告。
HTMLTestRunner支持python2.7。python3可以参见http://blog.51cto.com/hzqldjb/1590802来进行修改。
HTMLTestRunner.py 文件,下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
下载后将其放在testcase目录中去或者放入…\Python38\Lib 目录下(windows)
如需要该python 文件的得话,可评论或者私信
1.创建一个存放HTML报告得文件夹
2.解决重复命名的问题(用当前时间来命名)
3.报告的输出
错误截图API:get_screenshot_as_file()
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.common.by import By
import unittest, time, re
import os
class Baidu1(unittest.TestCase):
# test fixture,初始化环境
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(30)
self.base_url = "http://www.baidu.com/"
self.verificationErrors = []
self.accept_next_alert = True
# 测试用例,必须以test开头
def test_hao(self):
driver = self.driver
driver.get(self.base_url + "/")
driver.find_element(By.LINK_TEXT, "hao123").click()
time.sleep(2)
try:
self.assertEqual(u'hao_上网从这里开始', driver.title)
except:
self.savescreenshot(driver, 'hao.png')
# 判断element是否存在,可删除
def is_element_present(self, how, what):
try:
self.driver.find_element(by=how, value=what)
except NoSuchElementException as e:
return False
return True
# 判断alert是否存在,可删除
def is_alert_present(self):
try:
self.driver.switch_to_alert()
except NoAlertPresentException as e:
return False
return True
# 关闭alert,可删除
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = True
# test fixture,清除环境
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
def savescreenshot(self, driver, file_name):
if not os.path.exists('./image'):
os.makedirs('./image')
now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
# 截图保存
driver.get_screenshot_as_file('./image/' + now + '-' + file_name)
time.sleep(1)
if __name__ == "__main__":
# 执行用例
unittest.main()
数据驱动:用测试数据驱动测试用例
1.安装ddt
cmd中使用如下命令:pip install ddt
2.导包
from ddt import ddt, data, unpack, file_data
3.数据驱动的方式
@data(value):一次性传一个参数
@data(value1,value2,…):一次性传递多个参数,需要用**@unpack** 映射
@file_data(“json 文件”) :
@data(“解析数据的方法(TXT/CSV文件)”)
4.注意
txt/csv文档中开头要有data,以及文件的格式(在保存文件时更改或在代码中读取时转换)
json 文件:
[
"Hello",
"Goodbye"
]
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.common.by import By
import unittest, time, re
import os, sys, csv
from ddt import ddt, data, unpack, file_data
def getCsv(file_name):
rows = []
path = sys.path[0]
print(path)
with open(path + '/data/' + file_name, 'rt') as f:
readers = csv.reader(f, delimiter=',', quotechar='|')
next(readers, None)
for row in readers:
temprows = []
for i in row:
temprows.append(i)
rows.append(temprows)
return rows
# 引入ddt
@ddt
class ddttest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.implicitly_wait(30)
self.base_url = "http://www.baidu.com"
self.verificationErrors = []
self.accept_next_alert = True
# 测试用例,必须以test开头
# 增加ddt数据
# @data('selenium', u'测试中文', '9999999999')
# @data(2,3,4)
# 单变更时不使用unpack
# @data([3, 2], [4, 3], [5, 3])
# @data(*getCsv('test_baidu_data.txt'))
# 使用file_data需要在cmd窗口下运行,否则找不到文件
# @file_data('test_data_list.json')
@data(*getCsv('test_baidu_data.csv'))
@unpack
def test_hao(self, value, expected_value):
driver = self.driver
driver.get(self.base_url + "/")
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(2)
self.assertEqual(expected_value, driver.title)
print(expected_value)
print(driver.title)
# 判断element是否存在,可删除
def is_element_present(self, how, what):
try:
self.driver.find_element(by=how, value=what)
except NoSuchElementException as e:
return False
return True
# 判断alert是否存在,可删除
def is_alert_present(self):
try:
self.driver.switch_to.alert()
except NoAlertPresentException as e:
return False
return True
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to.alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally:
self.accept_next_alert = True
# test fixture,清除环境
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
def savescreenshot(self, driver, file_name):
if not os.path.exists('./image'):
os.makedirs('./image')
now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
# 截图保存
driver.get_screenshot_as_file('./image/' + now + '-' + file_name)
time.sleep(1)
if __name__ == "__main__":
# 执行用例
unittest.main()