unittest框架_我的总结

unittest 框架:

unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用。

作为单元测试的框架, unittest 也是可以对程序最小模块的一种敏捷化的测试。

在自动化测试中,我们虽然不需要做白盒测试,但是必须需要知道所使用语言的单元测试框架。

利用单元测试框架,创建一个类,该类继承unittest 的TestCase,这样可以把每个case看成是一个最小的单元, 由测试容器组织起来,到时候直接执行,同时引入测试报告。
在这里插入图片描述

  • test fixture:初始化和清理测试环境,比如创建临时的数据库,文件和目录等,其中 setUp() 和 setDown() 是最常用的方法
  • test case:单元测试用例,TestCase 是编写单元测试用例最常用的类
  • test suite:单元测试用例的集合,TestSuite 是最常用的类
  • test runner:执行单元测试
  • test report:生成测试报告

testbaidu1.py:

from selenium import webdriver
import unittest,time,os

from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException

class Baidu1(unittest.TestCase):
# test fixture,初始化环境
    def setUp(self):
        self.driver=webdriver.Firefox()
        self.driver.implicitly_wait(10)
        self.base_url="http://www.baidu.com/"
        self.verficationErrors=[]
        self.accept_next_alert=True

	# 测试用例,必须以 test 开头
    def test_baidusearch(self):
        driver=self.driver
        driver.get(self.base_url+"/")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(u"测试")
        driver.find_element_by_id("su").click()

    def test_hao(self):
        driver=self.driver
        driver.get(self.base_url+"/")
        driver.find_element_by_link_text("hao123").click()
        self.assertEqual(u"hao123_上网从这里开始",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
    
    # 关闭 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.verficationErrors)

    if __name__=="__main__":
        unittest.main()  
        #unittest.main(verbosity=2)
        #测试结果会显示的更加详细

批量执行脚本

构建测试套件

假设已经有了testbaidu1.py,testbaidu2.py两个文件,那么怎么同时执行这两个文件?

testbaidu2.py:

from selenium import webdriver
import time
import unittest
from selenium.common.exceptions import NoAlertPresentException
from selenium.common.exceptions import NoSuchElementException

class Baidu2(unittest.TestCase):
    # test fixture,初始化环境
    def setUp(self):
        self.driver=webdriver.Firefox()
        self.driver.implicitly_wait(10)
        self.base_url="http://baidu.com/"
        self.verficationErrors=[]
        self.accept_next_alert=True

    # 测试用例,必须以test开头
    def test_baidusearch(self):
        driver=self.driver
        driver.get(self.base_url+"/")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(u"selenium")
        # "u" 防止编码乱码
        driver.find_element_by_id("su").click()
        driver.find_element_by_id("su").click()

    # 判断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

    # text fixture,清除环境
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([],self.verficationErrors)

    if __name__=="__main__":
       unittest.main()
addTest() 的应用

当有多个或者几百测试用例的时候, 就需要一个测试容器( 测试套件) ,把测试用例放在该容器中进行执行,unittest 模块中提供了TestSuite 类来生成测试套件,使用该类的构造函数可以生成一个测试套件的实例,该类提供了addTest来把每个测试用例加入到测试套件中。

addTest()方法按照增加顺序来执行

runall.py:

from selenium import webdriver
import time
import unittest

# 导入testbaidu1,testbaidu2
import testbaidu1
import testbaidu2

# 手工添加案例到套件
def createsuite():
    suite=unittest.TestSuite()
    # 将测试用例加入到测试容器(套件)中
    suite.addTest(testbaidu1.Baidu1("test_baidusearch"))
    suite.addTest(testbaidu1.Baidu1("test_hao"))
    suite.addTest(testbaidu2.Baidu2("test_baidusearch"))
    return suite

if __name__=="__main__":
    suite=createsuite()
    runner=unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

两个不方便的地方,阻碍脚本的快速执行,必须每次修改runall.py:
1)需要导入所有的py文件,比如import testbaidu1,每新增一个需要导入一个
2)addTest需要增加所有的testcase,如果一个py文件中有10个case,就需要增加10次

makeSuite() 和 TestLoader() 的应用
  • 在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。
  • TestLoader 用于创建类和模块的测试套件,一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。

runall.py:

import unittest,csv 
import os,sys 
import time 
import testbaidu1 
import testbaidu2
 
# 手工添加案例到套件
def createsuite():
	suite = unittest.TestSuite()     
	# 将测试用例加入到测试容器(套件)中     
	suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))     
	suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))     
	return suite     
	'''     
	suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)  
	suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)      
	suite = unittest.TestSuite([suite1, suite2])     
	return suite     
	'''     

if __name__=="__main__":     
	suite=createsuite()     
	runner = unittest.TextTestRunner(verbosity=2)     
	runner.run(suite) 
discover() 的应用

discover 是通过递归的方式到其子目录中从指定的目录开始,找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为discover(dir,pattern,top_level_dir=None)

import unittest,csv 
import os,sys 
import time
 
# 手工添加案例到套件
def createsuite():     	
discover=unittest.defaultTestLoader.discover('../test',pattern='test*.py',top_level_dir=None)     
	print discover     
	return discover     

if __name__=="__main__":     
	suite=createsuite()     
	runner = unittest.TextTestRunner(verbosity=2)     
	runner.run(suite)
用例的执行顺序

unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为:0-9,A-Z,a-z。
所以,TestAdd 类会优先于TestBdd 类被发现,test_aaa() 方法会优先于test_ccc() 被执行。

忽略用例执行

@unittest.skip(“skipping”)
在执行程序的时候就不会在执行该部分

	@unittest.skip("skipping")
    def test_baidusearch(self):
        driver=self.driver
        driver.get(self.base_url+"/")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(u"测试")
        driver.find_element_by_id("su").click()

unittest断言

自动化的测试中,对于每个单独的case来说,一个case的执行结果中,必然会有期望结果实际结果,来判断该case是通过还是失败,在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果。
如果给定的断言通过,测试会继续执行到下一行的代码,如果断言失败,对应的case测试会立即停止或者生成错误信息( 一般打印错误信息即可) ,但是不要影响其他的case执行。

序号断言方法断言描述
1assertEqual(arg1, arg2, msg=None)验证arg1=arg2,不等则fail
2assertNotEqual(arg1, arg2, msg=None)验证arg1 != arg2, 相等则fail
3assertTrue(expr, msg=None)验证expr是true,如果为false,则fail
4assertFalse(expr,msg=None)验证expr是false,如果为true,则fail
5assertIs(arg1, arg2, msg=None)验证arg1、arg2是同一个对象,不是则fail
6assertIsNot(arg1, arg2, msg=None)验证arg1、arg2不是同一个对象,是则fail
7assertIsNone(expr, msg=None)验证expr是None,不是则fail
8assertIsNotNone(expr, msg=None)验证expr不是None,是则fail
9assertIn(arg1, arg2, msg=None)验证arg1是arg2的子串,不是则fail
10assertNotIn(arg1, arg2, msg=None)验证arg1不是arg2的子串,是则fail
11assertIsInstance(obj, cls, msg=None)验证obj是cls的实例,不是则fail
12assertNotIsInstance(obj, cls, msg=None)验证obj不是cls的实例,是则fail
self.assertEqual(u"hao_上网从这里开始",driver.title,msg="not equal !!!")
self.assertFalse("admin"="admin1",msg="True")

HTML报告生成

脚本执行完毕之后,还需要看到HTML报告,通过HTMLTestRunner.py 来生成测试报告。

import sys,os
from selenium import webdriver
import time
import unittest
import HTMLTestRunner

# 导入testbaidu1,testbaidu2
from src import testbaidu1
from src import testbaidu2

# 手工添加案例到套件
def createsuite():
discover=unittest.defaultTestLoader.discover("../src",pattern='testbaidu*.py',top_level_dir=None)
    print(discover)
    return discover

if __name__=="__main__":
    curpath=sys.path[0]
    # 取当前时间
    now=time.strftime("%Y-%m-%d-%H %M %S",time.locatiome(time.time()))

    if not os.path.exits(curpath+'/resultreport'):
        os.makedirs(curpath+'/resultreport')

    filename=curpath+'/resultreport'+now+'resultreport.html'
    with open(filename,'wb') as fp:
        runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'测试报告',description=u'用例执行情况',verbosity=2)
        suite=createsuite()
        runner.run(suite)

异常捕捉与错误截图

用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,会给我们错误定位带来方便。

from selenium import webdriver
import time,os
import unittest
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException

class Baidu1(unittest.TestCase):

    #test fixture,初始化环境
    def setUp(self):
        self.driver=webdriver.Firefox()
        self.driver.implicitly_wait(10)
        self.base_url="http://www.baidu.com"
        self.verficationErrors=[]
        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(3)
        try:
            self.assertEquals(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.drievr.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.verficationErrors)

    #异常捕捉与错误截图
    def savescreenshot(self,driver,file_name):
        if not os.path.exits('./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(3)

if __name__=="__main__":
    unittest.main()

数据驱动

考虑如下场景:
需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这时候需要编写3个案例吗?有没有版本一次运行?
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成。

ddt 的安装:

pip install ddt 
python setup.py install

dd.ddt:
装饰类,也就是继承自TestCase的类。

ddt.data:
装饰测试方法。参数是一系列的值。

ddt.file_data:

装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。
注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。

ddt.unpack:
传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理。

Testddt.py: 目录下建data文件夹,并建一个test_baidu_data.csv文件(用execl(最好用TXT文件)另存为csv)

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 
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].replace('\test','')    
	print path
 
    with open(path+'/data/'+file_name,'rb') as f:        
    	readers=csv.reader(f,delimiter=',',quotechar='|')        
    	next(readers,None)        
    	for row in readers:            
    		temprows=[]            
    		for i in row:                
    			temprows.append(i.decode('gbk'))            
    		rows.append(temprows)            
    		return rows 

# 引入ddt @ddt 
class Testddt(unittest.TestCase):    
	def setUp(self):        
		self.driver = webdriver.Firefox()        
		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.csv'))    
 	# 使用file_data需要在cmd窗口下运行,否则找不到文件    
 	# @file_data('test_data_list.json')    
 	@unpack 
 	def test_hao(self,value,expected_value):    
 	# def test_hao(self,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 

# 关闭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()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值