这里的示例以网易邮箱为例,可以自己注册一个来练习,要说明的是,直接在网易的首页进行定位是定不了的,网易进行过处理;这里是通过百度搜索在搜索结果中进行的定位。
unittest认识
unittest是python内置的单元测试框架,具备编写用例、组织用例、执行用例、输出报告等自动化框架的条件。
使用unittest前需要了解该框架的五个概念: 即test case,test suite,testLoader,test runner,test fixture。
unitest的工作原理
通过unittest类调用分析,可将框架的工作流程概况如下:
编写TestCase,由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite, 最后将运行的结果保存在TextTestResult中。
#coding:utf8
from selenium import webdriver # webdriver属于selenium的API
import unittest # unittest是python自带的模块
import time
class test_mail_login(unittest.TestCase): # 定义一个类并集成 unittest 基类中的 TestCase 类
def setUp(self): # 每个测试case运行之前运行(前置条件)
self.driver = webdriver.Chrome() # 驱动谷歌浏览器
self.driver.get("https://www.baidu.com") # 获取百度URL
self.driver.set_window_size(800, 1000) # 窗口大小设置
# self.driver.maximize_window() # 窗口最大化
time.sleep(2)
self.driver.find_element_by_id("kw").send_keys("网易") # 百度输入框定位并键入关键字
time.sleep(3)
self.driver.find_element_by_id("su").click() # 点击搜索按钮
self.driver.implicitly_wait(5) # 页面静置5秒,无任何动作
def test_mail_loginsuc(self): # 设计测试用例(case)重点注意,方法名必须以【test_】开头
self.driver.find_element_by_id("op_email3_username").send_keys("testerhunter")
time.sleep(2)
self.driver.find_element_by_class_name("op_email3_password").send_keys("123456hunter")
time.sleep(2)
self.driver.find_element_by_class_name("c-btn").click()
self.handles = self.driver.window_handles # 获取所有句柄
self.driver.switch_to_window(self.handles[-1]) # 句柄转换,获得当前句柄
time.sleep(3)
txt = self.driver.find_element_by_id("spnUid").text # 获取页面固定内容的文本
print(txt)
self.assertEqual(txt, "[email protected]") # 做断言处理,判断实际结果与期望结果是否一致
def tearDown(self): # 每个测试case运行完之后执行(后置条件)
self.driver.delete_all_cookies() # 清空所有cookies值,让页面更清洁,反应更快,避免造成缓存问题
self.driver.quit() # 关闭浏览器
if __name__ == '__main__':
unittest.main() # 这种执行方式,用例的执行顺序是无序的
#coding:utf8
from selenium import webdriver # webdriver属于selenium的API
import unittest # unittest是python自带的模块
import time
class test_mail_login(unittest.TestCase): # 定义一个类并集成 unittest 基类中的 TestCase 类
@classmethod
def setUpClass(cls): # 每个测试case运行之前运行(前置条件)
cls.driver = webdriver.Chrome() # 驱动谷歌浏览器
cls.driver.get("https://www.baidu.com") # 获取百度URL
cls.driver.set_window_size(800, 1000) # 窗口大小设置
# self.driver.maximize_window() # 窗口最大化
time.sleep(2)
cls.driver.find_element_by_id("kw").send_keys("网易") # 百度输入框定位并键入关键字
time.sleep(3)
cls.driver.find_element_by_id("su").click() # 点击搜索按钮
cls.driver.implicitly_wait(5) # 页面静置5秒,无任何动作
def test_mail_loginsuc(self): # 设计测试用例(case)重点注意,方法名必须以【test_】开头
self.driver.find_element_by_id("op_email3_username").send_keys("testerhunter")
time.sleep(2)
self.driver.find_element_by_class_name("op_email3_password").send_keys("123456hunter")
time.sleep(2)
self.driver.find_element_by_class_name("c-btn").click()
self.handles = self.driver.window_handles # 获取所有句柄
self.driver.switch_to_window(self.handles[-1]) # 句柄转换,获得当前句柄
time.sleep(3)
txt = self.driver.find_element_by_id("spnUid").text # 获取页面固定内容的文本
print(txt)
self.assertEqual(txt, "[email protected]") # 做断言处理,判断实际结果与期望结果是否一致
@classmethod
def tearDownClass(cls): # 每个测试case运行完之后执行(后置条件)
cls.driver.delete_all_cookies() # 清空所有cookies值,让页面更清洁,反应更快,避免造成缓存问题
cls.driver.quit() # 关闭浏览器
if __name__ == '__main__':
# 使用TestSuite可以控制用例的顺序,用例的执行顺序是由添加到TestSuite的顺序决定的
tests = [test_mail_login('test_mail_loginsuc')] # 以列表的形式添加用例
suite = unittest.TestSuite() # 实例化一个用例对象suite
# suite.addTest(test_mail_login('test_mail_loginsuc')) # 执行单个用例
suite.addTests(tests) # 将测试用例实例增加到测试套件中
runner = unittest.TextTestRunner() # 实例化一个运行的对象runner
runner.run(suite) # 执行用例对象
这里参考一篇博客(https://blog.51cto.com/2681882/2123613)做一个批量类型用例的展示,会涉及到导模块的问题:
待测模块【myfunc.py】:
#encoding=utf8
def is_prime(num):
if num < 0 or num in (0,1):
return False
for element in range(2,num):
if num % element == 0:
return False
return True
def add(a,b):
return a + b
def divide(a,b):
return a/b
编写unittest测试用例【test_myfunc.py】:
#encoding=utf8
import unittest
from myfunc import is_prime,add,divide
class TestMyFunc(unittest.TestCase):
def setUp(self):
print('每个用例执行前会调用setUp方法准备环境')
@classmethod
def setUpClass(cls):
print('所有用例执行前,只执行一次。')
def test_is_prime(self):
print('is_prime')
self.assertTrue(is_prime(5))
self.assertFalse(is_prime(8))
self.assertFalse(is_prime(0))
self.assertFalse(is_prime(1))
self.assertFalse(is_prime(-3))
def test_add(self):
print('add')
self.assertEqual(3, add(1,2))
self.assertEqual(3, add(2, 2))
def test_divide(self):
print('divide')
self.assertEqual(2,divide(6,3))
self.assertNotEqual(2,divide(5,2))
def tearDown(self):
print('每个用例执行后会调用tearDown方法进行环境清理')
@classmethod
def tearDownClass(cls):
print('所有用已执行完,只执行一次!')
if __name__ == '__main__':
tests = [TestMyFunc('test_is_prime'),TestMyFunc('test_add'),TestMyFunc('test_divide')]
suite = unittest.TestSuite().addTests(tests)
unittest.TextTestRunner().run(suite)
# unittest.main()
运行结果如下:
D:\xuexi\Python34\python.exe "D:\xuexi\PyCharm 2016.3.1\helpers\pycharm\utrunner.py" E:\Pythonworkspace\autotest\day01\test_myfunc.py true
Testing started at 9:23 ...
所有用例执行前,只执行一次。
每个用例执行前会调用setUp方法准备环境
add
每个用例执行后会调用tearDown方法进行环境清理
Failure
Traceback (most recent call last):
File "E:\Pythonworkspace\autotest\day01\test_myfunc.py", line 24, in test_add
self.assertEqual(3, add(2, 2))
AssertionError: 3 != 4
每个用例执行前会调用setUp方法准备环境
divide
每个用例执行后会调用tearDown方法进行环境清理
每个用例执行前会调用setUp方法准备环境
is_prime
每个用例执行后会调用tearDown方法进行环境清理
所有用已执行完,只执行一次!
Process finished with exit code 0
在unittest中,用例是以test开头的方法定义的,默认执行顺序是根据用例名称升序进行,如上面的用例, 实际执行顺序为:test_add-->test_divide-->test_is_prime,而不是用例定义的先后顺序。
使用unittest对myfunc进行单元测试,首先需要导入unitest框架和待测模块myfunc,定义的测试用例方法类 需要继承unittest.TestCase,且测试用例方法是以test开头作为标识,用例的执行结果以assetxxx断言结果 决定,如果断言返回为false,将抛出assetError异常(AssertionError: 3 != 4)。
注意:要是在导包遇到问题,比如上面的【from myfunc import is_prime,add,divide】会报红线,在编译的时候是通不过的,关于路径问题就自己解决了,一般都是放在相对路径下,这里放在相对路径下,并且要改变 package 的属性为 【sources root】:
需要将【HTMLTestRunner.py】下载并安放到python安装包的【lib】目录中即可使用,以下给出下载地址:
链接:https://pan.baidu.com/s/1SpW9PZpUgkrCAWK8cYlg6Q
提取码:6csz
对于以上测试用例输出HTML格式的输出报告,直接在 if 模块中加入报告输出内容即可,如果这种方式不可行,那么我们可以把运行内容单独提出来写一个模块,如下【run.py】模块内容:
import time
import unittest
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
testdir = './' # 定义相对路径为当前路径
discover = unittest.defaultTestLoader.discover(testdir, pattern='test_*.py') #查找当前路径下所有满足test_*.py格式的模块内的测试用例
now = time.strftime('%Y-%m-%d %H-%M-%S', time.localtime(time.time())) #格式化时间,为后面输出测试报告做准备
repoertpath = open(r'E:/autotest/' + now + '.html', 'wb') #指定测试报告的存放路径
runner = HTMLTestRunner(stream=repoertpath,title=u'自动化测试报告',description='testcase_excute !') #实例化测试报告模块的测试报告对象
runner.run(discover)
repoertpath.close()
现在把上面的中合起来,做一个跨包的测试用例编写即输出报告打印,用例代码就不给出来了,写出来即可,像以下模式:
需要引入【sys】模块进行路径下的文件调取:
#encoding:gbk
import HTMLTestRunner#导入测试报告模块
import unittest
import sys
import time
sys.path.append("./add/")#利用python标准库中的sys模块中的path来添加本地能识别的add目录
sys.path.append("./sbu/")
sys.path.append("./tester/")
sys.path.append("./day01")
testdir = './'#定义相对路径为当前路径
discover = unittest.defaultTestLoader.discover(testdir,pattern='test_*.py')#查找当前路径下所有满足test_*.py格式的模块内的测试用例
now = time.strftime("%Y-%m-%d %H-%M-%S",time.localtime(time.time()))#格式化时间,为后面输出测试报告做准备
repath = open(r"E:/autotest/"+now+".html","wb")#指定测试报告的存放路径
print(discover)
if __name__ == '__main__':
runner = HTMLTestRunner.HTMLTestRunner(stream=repath,title=u"自动化测试报告",description=u"对测试用例的输出报告分析")#实例化测试报告模块的测试报告对象
runner.run(discover)#执行上面跨目录找到的说有符合'test_*.py'模块中的测试用例,并将执行结果输出到测试报告
会遇到很多问题:诸如编码格式、输出报告打印不出来、能打印出报告但是内容为空等等,都需要我们去分析,调整。