一篇文章搞懂unittest单元测试框架

Python 2.1及以后的版本,将unittest作为一个标准模块放入Python开发包中。

01使用unittest编写测试用例【文末准备了一份自动化测试的学习资源免费分享】

规则:

  • import unittest
  • 创建一个测试类,必须要继承unittest.TestCase类
  • 创建一个测试方法,且方法要以“test” 开头
from calculator import Calculator
    import unittest
     
    class TestAdd(unittest.TestCase):
        def test_add(self):
            c =Calculator(3,5)
            result = c.add()
            self.assertEqual(result,8)
     
    if __name__ =='__main__':
        unittest.main() //通过main()方法来执行测试用例;按照测试类、方法的名称ASCII值大小的顺序执行用例

unittest的执行结果

  • “.” 表示测试用例执行通过
  • “F”表示执行失败
  • “E” 表示执行错误
  • "s“表示运行跳过

02三个重要概念

  • Test Case 最小的测试单元,即测试方法。 unittest提供了TestCase基类,我们创建的测试类要继承该基类,它可以用来创建新的测试用例。
  • Test Suite 测试用例、测试套件或两者的集合,用于组装一组要运行的测试。 使用TestSuite类来创建测试套件。
  • Test Runner Test Runner是一个组件,用于协调测试的执行并向用户提供结果。 unittest提供了TextTestRunner类运行测试用例。

03测试用例执行顺序

unittest默认按照ASCII码的顺序加载测试用例(包括测试目录和测试文件、测试类、测试方法),即它并不是按照测试用例的创建顺序从上到下执行的。 discover() 和 main()方法的执行顺序是一样的。故想让某个测试文件先执行,可以在命名上加以控制。

如何控制测试用例的执行顺序?

  • 可以通过TestSuite类的addTest()方法按照一定的顺序来加载测试用例,这样想先被执行的用例就可以先加载。
  from calculator import Calculator
    import unittest
     
    class TestAdd(unittest.TestCase):
        def test_add(self):
            c =Calculator(3,5)
            result = c.add()
            self.assertEqual(result,8)
     
        def test_add_decimals(self):
            c=Calculator(3.5,5.5)
            result=c.add()
            self.assertEqual(result,9)
     
    class TestSub(unittest.TestCase):
        def test_sub(self):
            c =Calculator(5,1)
            result = c.sub()
            self.assertEqual(result,4)
     
    if __name__ =='__main__':
        #创建测试套件
        suit = unittest.TestSuite()
        suit.addTest(TestSub("test_sub")) //添加测试用例
        suit.addTest(TestAdd("test_add_decimals"))
     
        #创建测试运行器
        runner = unittest.TextTestRunner()
        runner.run(suit)

04执行多个测试用例

unittest.defaultTestLoader.discover()方法可以从多个文件中查找测试用例。

该类根据各种标准加载测试用例,并将它们返回给测试套件

discover(start_dir, pattern='Test*.py', top_level_dir=None)

  • start_dir:待测试的模块名/测试用例目录; discover()方法会自动根据这个参数查找测试用例文件
  • pattern:测试用例文件名的匹配原则
  • top_level_dir:测试模块的顶级目录,如果没有顶级目录,默认为None
import unittest
    test_dir='Test' //文件目录
     
    suits =unittest.defaultTestLoader.discover(test_dir, pattern='Test*.py')
     
    if __name__=='__main__':
        runner = unittest.TextTestRunner()
        runner.run(suits)

如果想让discover()查找子目录下的测试文件,得将子目录标记为一个python模块(子目录下放__init__.py文件)

05跳过测试和预期失败

import  unittest
    class MyTest(unittest.TestCase):
     
        @unittest.skip("跳过这条用例")
        def test_skip(self):
            print('aaa')
     
        @unittest.skipIf(3>2,"当条件为真时跳过测试")
        def test_skip_if(self):
            print('bbb')
     
        @unittest.skipUnless(3>2,"当条件为假时跳过测试")
        def test_skip_unless(self):
            print('ccc')
     
    // 不论执行结果是什么,都将测试标记为失败
        @unittest.expectedFailure 
        def test_fail(self):
            print('ddd')
     
    if __name__=="__main__":
        unittest.main()

执行结果:

E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/skip_fail.py
    ddd
    ussccc
    .
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
     
    FAILED (skipped=2, unexpected successes=1)

以上四个装饰器同样适用于测试类。

06Setup和Teardown

  • setUpModule/tearDownModule 在整个模块的开始与结束时被执行
  • setUpClass/ tearDownClass 在测试类的开始与结束时被执行
  • setUp/tearDown 在测试用例的开始与结束时被执行
import unittest
    def setUpModule():
        print(" Module start .....")
     
    def tearDownModule():
        print(" Module end ...")
     
    class MyTest(unittest.TestCase):
     
        @classmethod
        def setUpClass(cls):
            print("Class Start...")
        
        @classmethod
        def tearDownClass(cls):
            print("Class end...")
     
        def setUp(self):
            print("test case start ...")
     
        def tearDown(self):
            print("test case end ...")
     
        def testcase1(self):
            print("this is first test case")
        
        def testcase2(self):
            print("this is second test case")
     
    if __name__ == "__main__":
        unittest.main()

执行结果如下:

E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/setup.py
    Module start .....
    Class Start...
    test case start ...
    this is first test case
    test case end ...
    .test case start ...
    this is second test case
    test case end ...
    .Class end...
     Module end ...
     
 ----------------------------------------------------------------------
    Ran 2 tests in 0.002s
     
    OK

07Web自动化测试

 import unittest
    from selenium import webdriver
    from time import sleep
     
    class Baidu(unittest.TestCase):
     
        @classmethod
        def setUpClass(cls):
            cls.driver = webdriver.Chrome()
            cls.driver.implicitly_wait(5)
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window()
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,text):
            self.driver.find_element_by_id("kw").clear()
        self.driver.find_element_by_id("kw").send_keys(text)
            self.driver.find_element_by_id("su").click()
            sleep(3)
     
        def test_search_selenium(self):
            search_key="selenium"
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
        def test_search_python(self):
            search_key="python"
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
    if __name__ =="__main__":
        unittest.main()

08Parameterized

Parameterized是python的一个参数化库,同时支持unittest、pytest单元测试框架。

  • pip install parameterized
  • parameterized.expand() 加载数据,列表中每个元组是一条测试用例
import unittest
    from time import sleep
    from selenium import webdriver
    from parameterized import parameterized
     
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @parameterized.expand(
            [
                ("case1","selenium"),
                ("case2","python")
            ]
        )
        def test_search(self,name,search_key):
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  

09DDT

Data-Driven Tests 是针对unittest单元测试框架设计的扩展库。

安装:

pip install ddt

导入:

from ddt import ddt, data, file_data,unpack

使用规则:

  • 必须用 @ddt 装饰测试类
  • 数据有不同形式的参数化,如下方的元组、列表、字典类型
  • @data( 数据列表/元组/字典,字典的key和方法中的形参名称保持一致)
  • @unpack 装饰测试方法
import unittest
    from time import sleep
    from selenium import webdriver
    from ddt import ddt,data,file_data,unpack
     
    @ddt
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @data(
                ("case1","selenium"),
                ("case2","python")
        )
        @unpack
        def test_search1(self,name,search_key):
            print("第一组测试用例:",name)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
        @data(
                ["case1","selenium"],
                ["case2","python"]
        )
        @unpack
        def test_search2(self,name,search_key):
            print("第二组测试用例:",name)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
        @data(
                {"key":"selenium"},
                {"key":"python"}
        )
        @unpack
        def test_search3(self,key):
            print("第三组测试用例:",key)
            self.search(key)
            self.assertEqual(self.driver.title,key+"_百度搜索")
     
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  

执行报错如下时,是因为文件名也为ddt:

PS E:\selenium> & "D:/Program Files/Python39/python.exe" e:/selenium/Test/unittest/ddt.py
    Traceback (most recent call last):
      File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
        from ddt import ddt,data,file_data,unpack
      File "e:\selenium\Test\unittest\ddt.py", line 4, in <module>
        from ddt import ddt,data,file_data,unpack
   
 ImportError: cannot import name 'ddt' from partially initialized module
 'ddt' (most likely due to a circular import) 
(e:\selenium\Test\unittest\ddt.py)    

10数据文件的参数化

@file_data()装饰器中内容为文件名称。支持json格式和yaml格式。

import unittest
    from time import sleep
    from selenium import webdriver
    from ddt import ddt,data,file_data,unpack
     
    @ddt
    class TestBaidu(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.driver=webdriver.Chrome()
            cls.driver.get("http://www.baidu.com")
            cls.driver.maximize_window
     
        @classmethod
        def tearDownClass(cls):
            cls.driver.quit()
     
        def search(self,search_key):
            self.driver.find_element_by_id("kw").clear()
            self.driver.find_element_by_id("kw").send_keys(search_key)
            self.driver.find_element_by_id("su").click()
            sleep(5)
     
        @file_data('ddt_data.json')
        @unpack
        def test_search1(self,search_key):
            print("第一组测试用例:",search_key)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
     
        @file_data('ddt_data_yml.yaml')
        @unpack
        def test_search2(self,case):
            search_key=case[0]["search_key"]
            print("第二组测试用例:",search_key)
            self.search(search_key)
            self.assertEqual(self.driver.title,search_key+"_百度搜索")
    if __name__ == '__main__':  
        unittest.main(verbosity=2)  
ddt_data.json
    {
        "case1": {"search_key": "python"},
        "case2": {"search_key": "ddt"}
    }
ddt_data_yml.yaml
    case1:
     - search_key: "python"
    case2:
     - search_key: "ddt" 

下面是配套资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!å¨è¿éæå¥å¾çæè¿°

最后: 可以在公众号:程序员小濠 ! 免费领取一份216页软件测试工程师面试宝典文档资料。以及相对应的视频学习教程免费分享!,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!喜欢软件测试的小伙伴们,可以加入我们的测试技术交流扣扣群:779450660里面有各种软件测试资源和技术讨论)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值