zoukankan      html  css  js  c++  java
  • Python:单元测试框架unittest

    1、什么是单元测试

      测试函数/方法或者一段代码,用于检验被测代码的一个很小的、很明确的功能是否正确,通常是开发做。

      在Python中的单元测试框架有Unittest和Pytest,现在总结Unittest。

    2、单元测试框架

    1、Unittest框架的五个概念

    •   test fixture:测试固件

    •   test case:测试用例

    •   test suit:测试套件

    •   test runner:测试运行器

    •   test loader:测试加载器

    1) Test Case

      一个TestCase 的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。单元测试(unittest)的本质也就在这里。一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。

    2) Test Suite

      多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套Test Suite。

    3) Test runner

      是用来执行测试用例的,其中的run(test)会执行TestSuite/TestCase 中的run(result) 方法。

     4) Test Loader

      是用来加载TestCase到Test Suite中的,其中几个 loadTestsFrom__() 方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite 实例。 

    5) Test fixture

      对一个测试用例环境的搭建和销毁,是一个fixture,通过覆盖Test Case的setUp() 和tearDown() 方法来实现。

    2、Unittest中的两个方法

    方法1:setup 和teardown

    1)结构:

    测试基本setup方法:第一个测试开始之前只执行一次
    多个类
    类:
        类setup方法:在当前类的第一个测试方法调用前执行,只执行一次
    
            setup方法:在执行测试方法前的一些环境或者数据相关的准备
            测试的方法:每个测试方法执行前,都会执行setup方法,然后执行teardown方法
            teardown方法:在执行方法后的一些环境或者数据相关的清理
    
        类teardown方法:在当前类的最后一个测试方法调用后执行,只执行一次
    
    测试级别teardown方法:所有的测试结束之后只执行一次

    2)说明

      setUp:实现测试前的初始化工作

      tearDown:实现测试结束后的清理工作

      用例之间按用例ASCII码的顺序加载,数字和字母顺序为 0~9,A~Z,a~z。

    方法2:断言方法

     3、加载测试用例的四种方法

      首先实例化:

    suite = unittest.TestSuite()
    loader = unittest.TestLoader()

      方法一:通过测试用例类进行加载

    suite.addTest(loader.loadTestsFromTestCase(MyTest1))
    suite.addTest(loader.loadTestsFromTestCase(MyTest2))

      方法二:通过测试用例模版去加载

    suite.addTest(loader.loadTestsFromTestModule(MyTest1))
    suite.addTest(loader.loadTestsFromTestModule(MyTest2))

      方法三:通过路径加载

    import os
    path = os.path.dirname(os.path.abspath(__file__))
    suite.addTest(loader.discover(path))

      方法四:逐条加载 low

    case1 = MyTest1("test1")
    case2 = MyTest1("test2")
    suite.addTest(case1)
    suite.addTest(case2)

    3、代码举例

    举例1:

    #encoding=utf-8
    import unittest
    
    # 被测试类
    class myclass(object):
        @classmethod #可以不加,但是调用前需要实例化
        def sum(self, a, b):
            return a + b #将两个传入参数进行相加操作
    
        @classmethod #可以不加,但是调用前需要实例化
        def sub(self, a, b):
            return a - b #将两个传入参数进行相减操作
    
    
    class mytest(unittest.TestCase):
        @classmethod #必须加
        def setUpClass(cls):#每个测试类的setup方法,只会执行一次
            "初始化类固件,固件是准备的意思"
            print ( "----setUpClass")
    
        @classmethod #必须加
        def tearDownClass(cls):#每个测试类的tearDown方法,只会执行一次
            "重构类固件"
            print ( "----tearDownClass")
    
        # 初始化工作
        def setUp(self):#每个测试方法均会执行一次
            self.a = 3
            self.b = 1
            print ( "--setUp")
    
        # 退出清理工作
        def tearDown(self):#每个测试方法均会执行一次
            print ( "--tearDown")
    
        # 具体的测试用例,一定要以test开头
        def testsum(self): #每个测试用例执行成功一次,打印一个 .
            # 断言两数之和的结果是否是4
            self.assertEqual(myclass.sum(self.a, self.b), 4, 'test sum fail')
    
        def testsub(self):
            # 断言两数之差的结果是否是2
            self.assertEqual(myclass.sub(self.a, self.b), 2, 'test sub fail')
    
    
    if __name__ == '__main__':
        unittest.main() # 启动单元测试

    执行结果:

    举例2:跳过指定的测试用例的三种方法

    # coding=utf-8
    import random
    import unittest    
    import sys
    
    class TestSequenceFunctions(unittest.TestCase):
        a = 6
    
        def setUp(self):
            self.seq = list(range(10))
    
        @unittest.skip("skipping") # 无条件忽略该测试方法
        def test_shuffle(self):
            random.shuffle(self.seq)
            self.seq.sort()
            self.assertEqual(self.seq, list(range(10)))
            self.assertRaises(TypeError, random.shuffle, (1, 2, 3))
    
        # 如果变量a > 5,则忽略该测试方法
        @unittest.skipIf(a > 5, "condition is not satisfied!")
        def test_choice(self):
            element = random.choice(self.seq)
            self.assertTrue(element in self.seq)
    
        # 除非执行测试用例的平台是Linux平台,否则忽略该测试方法  win32是windows
        @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") #被忽略时打印 第二个参数
        def test_sample(self):
            with self.assertRaises(ValueError):
                random.sample(self.seq, 20)
            for element in random.sample(self.seq, 5):
                self.assertTrue(element in self.seq)
    
    
    if __name__ == '__main__':
        # unittest.main()
        # 指定加载哪几个测试类,生成测试集合
        suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
        suite = unittest.TestSuite(suite)
        unittest.TextTestRunner(verbosity = 2).run(suite)

    结果:

    D:学习自动化单元测试>py -3 e4_unittest_skip.py
    test_choice (__main__.TestSequenceFunctions) ... skipped 'condition is not satisfied!'
    test_sample (__main__.TestSequenceFunctions) ... skipped 'requires Linux'
    test_shuffle (__main__.TestSequenceFunctions) ... skipped 'skipping'
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.003s
    
    OK (skipped=3)

    举例3:常用断言举例

    #encoding=utf-8
    import unittest
    import random
    
    # 被测试类
    class MyClass(object):
        @classmethod
        def sum(self, a, b):
            return a + b
    
        @classmethod
        def div(self, a, b):
            return a / b
    
        @classmethod
        def retrun_None(self):
            return None
    
    # 单元测试类
    class MyTest(unittest.TestCase):
    
        # assertEqual()方法实例
        def test_assertEqual(self):
            # 断言两数之和的结果
            # 一般不需要捕获异常,unittest会自动捕获异常
            try:
                a, b = 1, 2
                sum = 3
                self.assertEqual(a + b, sum, '断言失败,%s + %s != %s' %(a, b, sum))
            except AssertionError as e:
                print (e)
    
        # assertNotEqual()方法实例
        def test_assertNotEqual(self):
            # 断言两数之差的结果
            try:
                a, b = 5, 2
                res = 1
                self.assertNotEqual(a - b, res, '断言失败,%s - %s != %s' %(a, b, res))
            except AssertionError as e:
                print (e)
    
        # assertTrue()方法实例
        def test_assertTrue(self):
            # 断言表达式的为真
            try:
                self.assertTrue(1 == 1, "表达式为假")
            except AssertionError as e:
                print (e)
    
        # assertFalse()方法实例
        def test_assertFalse(self):
            # 断言表达式为假
            try:
                self.assertFalse(3 == 2, "表达式为真")
            except AssertionError as e:
                print (e)
    
        # assertIs()方法实例
        def test_assertIs(self):
            # 断言两变量类型属于同一对象
            try:
                a = 12
                b = a
                self.assertIs(a, b, "%s与%s不属于同一对象" %(a, b))
            except AssertionError as e:
                print (e)
    
        # test_assertIsNot()方法实例
        def test_assertIsNot(self):
            # 断言两变量类型不属于同一对象
            try:
                a = 12
                b = "test"
                self.assertIsNot(a, b, "%s与%s属于同一对象" %(a, b))
            except AssertionError as e:
                print (e)
    
        # assertIsNone()方法实例
        def test_assertIsNone(self):
            # 断言表达式结果为None
            try:
                result = MyClass.retrun_None()
                self.assertIsNone(result, "not is None")
            except AssertionError as e:
                print (e)
    
        # assertIsNotNone()方法实例
        def test_assertIsNotNone(self):
            # 断言表达式结果不为None
            try:
                result = MyClass.sum(2, 5)
                self.assertIsNotNone(result, "is None")
            except AssertionError as e:
                print (e)
    
        # assertIn()方法实例
        def test_assertIn(self):
            # 断言对象A是否包含在对象B中
            try:
                strA = "this is a test"
                strB = "is"
                self.assertIn(strB, strA, "%s不包含在%s中" %(strB, strA))
            except AssertionError as e:
                print (e)
    
        # assertNotIn()方法实例
        def test_assertNotIn(self):
            # 断言对象A不包含在对象B中
            try:
                strA = "this is a test"
                strB = "Selenium"
                self.assertNotIn(strB, strA, "%s包含在%s中" %(strB, strA))
            except AssertionError as e:
                print (e)
    
        # assertIsInstance()方法实例
        def test_assertIsInstance(self):
            # 测试对象A的类型是否值指定的类型
            try:
                x = MyClass
                y = object
                self.assertIsInstance(x, y, "%s的类型不是%s" %(x, y))
            except AssertionError as e:
                print (e)
    
        # assertNotIsInstance()方法实例
        def test_assertNotIsInstance(self):
            # 测试对象A的类型不是指定的类型
            try:
                a = 123
                b = str
                self.assertNotIsInstance(a, b, "%s的类型是%s" %(a, b))
            except AssertionError as e:
                print (e)
    
        # assertRaises()方法实例
        def test_assertRaises(self):
            # 测试抛出的指定的异常类型
            # assertRaises(exception)
            with self.assertRaises(TypeError) as cm:
                random.sample([1,2,3,4,5], "j")
            # 打印详细的异常信息
            #print "===", cm.exception
    
            # assertRaises(exception, callable, *args, **kwds)
            try:
                self.assertRaises(ZeroDivisionError, MyClass.div, 3, 0)
            except ZeroDivisionError as e:
                print (e)
    
        # assertRaisesRegexp()方法实例
        def test_assertRaisesRegexp(self):
            # 测试抛出的指定异常类型,并用正则表达式具体验证
            # assertRaisesRegexp(exception, regexp)
            with  self.assertRaisesRegex(ValueError, 'literal') as ar:
                int("xyz")
            # 打印详细的异常信息
            #print ar.exception
            # 打印正则表达式
            #print "re:",ar.expected_regexp
    
            # assertRaisesRegexp(exception, regexp, callable, *args, **kwds)
            try:
                self.assertRaisesRegexp(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
            except AssertionError as e:
                print (e)
    
    
    if __name__ == '__main__':
        # 执行单元测试
        unittest.main()

    举例4:生成图形化测试报告

    导入文件HTMLTestRunner.py ;放到python的安装目录下(D:Python37-32Libsite-packages)

    运行对应的主方法:

    import unittest
    
    import HTMLTestRunner
    
    if __name__ == '__main__' :
        # 加载当前目录下所有有效的测试模块(以test开头的文件),“.”表示当前目录
        testSuite = unittest.TestLoader().discover('.')
        filename = "test.html"  # 定义个报告存放路径,支持相对路径。
        # 以二进制方式打开文件,准备写
        fp = open(filename, 'wb')
        # 使用HTMLTestRunner配置参数,输出报告路径、报告标题、描述,均可以配
        runner = HTMLTestRunner.HTMLTestRunner(stream = fp,
            title = 'Report_title', description = 'Report_description')
        # 运行测试集合
        runner.run(testSuite)

    结果:

     举例5:单元测试框架跑UI测试

    #encoding=utf-8
    import unittest
    from selenium import webdriver
    import time
    
    class GloryRoad(unittest.TestCase):
        def setUp(self):
            # 启动Chrome浏览器
            # 驱动文件位置要改,本地自己的驱动
            self.driver = webdriver.Chrome()
    
        def testSoGou(self):
            # 访问搜狗首页
            self.driver.get("http://sogou.com")
            # 清空搜索输入框默认内容
            self.driver.find_element_by_id("query").clear()
            # 在搜索输入框中输入“光荣之路自动化测试”
            self.driver.find_element_by_id("query").send_keys(u"WebDriver实战宝典")
            # 单击“搜索”按钮
            self.driver.find_element_by_id("stb").click()
            # 等待3秒
            time.sleep(3)
            assert u"吴晓华" in self.driver.page_source, u"页面中不存在要寻找的关键字!".encode("gbk")
    
    
        def testBing(self):
            # 访问bing首页
            self.driver.get("http://cn.bing.com")
            # 清空搜索输入框默认内容
            self.driver.find_element_by_id("sb_form_q").clear()
            # 在搜索输入框中输入“光荣之路自动化测试”
            self.driver.find_element_by_id("sb_form_q").send_keys(u"WebDriver实战宝典")
            # 单击“搜索”按钮
            self.driver.find_element_by_id("sb_form_go").click()
            # 等待3秒
            time.sleep(3)
            assert u"吴晓华" in self.driver.page_source, u"页面中不存在要寻找的关键字!".encode("gbk")
    
    
        def tearDown(self):
            # 退出浏览器
            self.driver.quit()
    
    if __name__ == '__main__':
        unittest.main()

     举例6:单元测试和ddt数据驱动结合

    安装包:pip3 install ddt

    代码:

    import unittest
    import ddt
    
    
    @ddt.ddt
    class Praddt(unittest.TestCase):
    
        def setUp(self):
            print("my test start!")
    
        def tearDown(self):
            print("my test complete!")
    
        @ddt.data(["admin", "1qaz", "OK"],  #一组数据,每一行都会触发运行
                  ["admin", "", "ERROR"],   #另一组数据
                  ["1", "1qaz", "ERROR"],   #另一组数据..
                  ["admin", "1234", "ERROR"],
                  ["Admin", "1qaz", "ERROR"]) #实现了数据驱动,可以执行5次
        
        @ddt.unpack
        def test_ddt(self, user, passwd, expect_value):
            print(user,passwd,expect_value)
            self.assertTrue(len(user)>0)
    
    
    if __name__ == '__main__':
        # 执行单元测试
        unittest.main()

    测试结果:

  • 相关阅读:
    软件测试经典面试题汇总测试…(转载)
    loadrunner录制时弹出invalid application path
    【转】性能测试知多少:性能分析与…(转载)
    loadrunner小知识累积(转载)
    selenium定位方法总结
    [testng]Cannot find class in classpath
    java工程名前有红色感叹号
    jenkins slave节点问题总结
    scrapy爬虫框架
    Python监听鼠标键盘事件
  • 原文地址:https://www.cnblogs.com/hqq2019-10/p/13949230.html
Copyright © 2011-2022 走看看