unittest测试框架总结

unittest框架解析

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

作为单元测试的框架, unittest 也是可以对程序最小模块的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须需要知道所使用语言的单元测试框架。利用单元测试框架,创建一个类,该类继承unittest的TestCase,这样可以把每个case看成是一个最小的单元, 由测试容器组织起来,到时候直接执行,同时引入测试报告。

unittest各组件的关系为:
在这里插入图片描述
(1)test fixtrue:初始化和清理测试环境,比如创建临时的数据库,文件和目录等,其中==setUp()和setDown()==是最常用的方法。
(2)test case:单元测试用例,TestCase是编写单元测试最常用的类。
(3)test suite:单元测试用例的集合。TestSuite是最常用的类
(4)test runner:执行单元测试
(5)test report:生成测试报告

批量执行脚本

构建测试套件

完整的单元测试很少只执行一个测试用例,开发人员通常都需要编写多个测试用例才能对某一软件功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在unittest中是用TestSuite 类来表示的。

假设已经有了Baidu1.py和Baidu2.py:
Baidu1.py

__author__ = "sunraylily"
from selenium import webdriver
import time
# 导入unittest包
import unittest
# 类名可以与文件名不同
class Baidu1(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开头

    def test_baidusearch(self):
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").send_keys(u"张杰")
        driver.find_element_by_id("su").click()

    # @unittest.skip("skipping")
    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, msg = "hao123")

    # 清除环境
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors, msg = u"出错")
if __name__ == "__main__":
     # 执行用例
    #  verbosity参数可以表示测试结果的信息复杂度
    unittest.main()

Baidu2.py

from selenium import webdriver
import time
import unittest

class Baidu2(unittest.TestCase):
    # 数据初始化
    def setUp(self) -> None:
        self.driver = webdriver.Chrome()
        self.base_url = "http://www.baidu.com/"
        self.verificationErrors = []
        self.accept_next_alert = True
#     测试用例,必须以test开头
    def test_baidusearch(self):
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").send_keys(u"谢娜")
        driver.find_element_by_id("su").click()

    def tearDown(self) -> None:
        self.driver.quit()
        self.assertEqual([], self.verificationErrors, msg = u"出错")

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

我们已经有了Baidu1.py和Baidu2.py,那么如何同时执行这两个文件呢?(也就是批量执行)

批量执行脚本有如下几种方法:
**(1)利用addTest():**unittest中提供了TestSuit类来生成测试套件,使用该类的构造方法可以生成一个测试套件的实例,该类的addTest()方法可以将每个测试用例加入到测试套件中。

该方法有两个不方便的地方:
(1)需要导入所有的py文件,比如import test_baidu1,每新增一个就要导入一个
(2)addTest()需要增加所有的testCase,如果一个py文件中有10个case,就需要增加10个。

**(2)makeSuite():**unittest测试框架中提供了makeSuite()方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。然后再利用addTest()进行添加。

(3)TestLoader():用于创建类和模块的测试套件,一般的情况下,使用TestLoader().loadTestsFromTestCase(TestClass)
来加载测试类。

经过makeSuite()和TestLoader()的引入,一个py文件测试类,只需要导入一次即可。

**(4)discover()的应用:**discover()是通过递归的方法到其子目录开始,找到所有测试模块并返回一个包含他们对象的TestSuite,然后进行模式怕匹配唯一的测试文件
discover的参数分别为:discover(dir,pattern,top_level_dir=None)

利用discover()测试类也不用每次添加指定,传入目录,进行匹配。
import os
import sys
import unittest
import time
from HTMLTestRunner import HTMLTestRunner
from src2.test import test_baidu1
from src2.test import test_baidu2

# 手工添加案例到套件
def createsuite():
    suite = unittest.TestSuite()
    # 将测试用例添加到测试套件中
    # # 1.addtest 按照添加顺序执行
    # suite.addTest(0416test1.Baidu1("test_hao"))
    # suite.addTest(0416test1.Baidu1("test_baidusearch"))
    # suite.addTest(0416test2.Baidu2("test_baidusearch"))
    # return suite

    # # 2.makeSuite,makeSuite可以将测试用例类中所有测试case组成测试套件
    # suite.addTest(unittest.makeSuite(0416test1.Baidu1))
    # suite.addTest(unittest.makeSuite(0426tes2.Baidu2))
    # return suite

    # #3.TetsLoader用于创建类和模块的测试套件
    # suite1 = unittest.TestLoader().loadTestsFromTestCase(0416test1.Baidu1)
    # suite2 = unittest.TestLoader().loadTestsFromTestCase(0416test2.Baidu2)
    # suite = unittest.TestSuite([suite1,suite2])
    # return suite

    # #4.discover通过递归方式到其子目录中从指定目录开始,找到所有测试模块并返回一个包含他们对象的TestSuite
    # # 然后进行加载与模式匹配唯一的测试文件
    discover = unittest.defaultTestLoader.discover('../test', pattern='test*.py', top_level_dir=None)
    print(discover)
    return discover

if __name__ == "__main__":
    # 生成HTML报告
    curpath = sys.path[0]
    # 取当地时间
    now = time.strftime("%Y-%m-%d-%H %M %S", time.localtime(time.time()))
    if not os.path.exists(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 = unittest.TextTestRunner(verbosity=2)
        runner.run(suite)
可以增加verbosity参数,例如unittest.main(verbosity=2)
在主函数中,直接调用main() ,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。
这里的verbosity 是一个选项, 表示测试结果的信息复杂度,有三个值:
0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个失败,20 成功80
1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ . ” 每个失败的用例前面有个“F”
2 ( 详细模式): 测试结果会显示每个测试用例的所有相关的信息
用例的执行顺序

unittest框架默认加载测试用例的顺序是根据ASCII码的顺序,数字和字母的顺序为:0-9,A-Z,a-z。
所以,TestAdd 类会优先于TestBdd 类被发现,test_aaa() 方法会优先于test_ccc() 被执行。对于测试目录与测试文件来说, unittest 框架也是按照这个规则来加载测试用例。

忽略用例执行(skip)
#在要忽略的case前面加上skip
@unittest.skip("skipping")
unittest断言

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

常用的断言:
在这里插入图片描述

self.assertEqual("admin", driver.find_element_by_link_text("admin").text)
HTML报告生成

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

用法见上面批量执行脚本的代码

异常捕捉与错误截图

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

例如编写一个函数,关键语句为driver.get_screenshot_as_file

 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)

举例:

# -*- 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
import unittest, time, re
import os
class Baidu1(unittest.TestCase):
#test fixture,初始化环境
 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开头 
 
 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()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值