zoukankan      html  css  js  c++  java
  • 01-unittest单元测试框架

    1. unittest框架

    unittestpython中一款内置的测试框架,用来测试代码的运行正确与否。他能够收集测试用例,执行测试用例,查看用例执行结果,还能够添加用例执行的前置条件和后置处理条件。

    unittest中的4大核心概念:

    • TestCase:测试用例,使用这类来编写测试用。它是对一类测试用例进行了封装,例如针对登录操作的测试用例,封装成一个class,针对注册 操作的测试用例,再封装成一个class;且每个class都要继承unittest.TestCase才行。每一条测试用例都是一个函数。
    import unittest
    
    class TestLogin(unittest.TestCase):
        def test_login(self):
            pass
    
    • TestSuit:测试套件,它的本质就是一个容器,里面不存放别的东西,只存放测试用例。
    • TextTestRunner:用例运行器,用来运行测试用例,并呈现出执行结果。
    • Fixture:用例执行的前置和后置条件。
    • TestLoader:用来收集用例。是用来加载TestCaseTestSuite中的。

    2. 简单使用

    1. unittestTestCase使用规范:

    1. 每一个测试类,都要继承unittest.TestCase
    2. 测试类中,每一个函数都要以test_开头,来代表一条测试用例;
    3. 在编写测试用例的时候,需要遵循以下规则:
      1. 要有测试数据;
      2. 要有测试步骤,一般的,测试步骤就是来调用已经完成的功能函数;
      3. 对最后的结果进行断言:只要的最后的结果出现AssertionError的错误,那就是用例失败;
      4. 断言可以使用assert关键字跟表达式来表达,assert如果是True,则表示用例通过,否则是用例失败;
      5. 但是,在使用unittest框架,框架的内部已经为我们封装好了,我们可以直接使用进行用例断言。

    一个简单的用例实例:

    # 功能函数
    def login_check(username=None, password=None):
        """ 登录校验的函数
        :param username: 账号
        :param password: 密码
        :return: dict type
        """
        if username != None and password != None:
            if username == 'python27' and password == 'lemonban':
                return {"code": 0, "msg": "登录成功"}
            else:
                return {"code": 1, "msg": "账号或密码不正确"}
        else:
            return {"code": 2, "msg": "所有的参数不能为空"}
    
    import unittest
    from Homework.class_unittest_study import login
    
    # 这是一个用例类,其中的每一个函数就代表着一个测试用例。
    class LoginTest(unittest.TestCase):
    
        # 正确用户名和密码
        def test_login_cor(self):
            res = login.login_check("python27", "lemonban")
            self.assertEqual(res, {"code": 1, "msg": "登录成功"}, msg="断言失败")
    
        # 用户名密码 都为空
        def test_login_no_pwdAndUsr(self):
            res = login.login_check()
            self.assertEqual(res, {"code": 2, "msg": "所有的参数不能为空"}, msg="断言成功")
    
        # 错误的用户名
        def test_login_wrong_usr(self):
            res = login.login_check("python30", "lemonban")
            self.assertEqual(res, {"code": 1, "msg": "账号或密码不正确"}, msg="断言成功")
    
        # 错误的密码
        def test_login_wrong_pws(self):
            res = login.login_check("python27", "123456")
            self.assertEqual(res, {"code": 1, "msg": "账号或密码不正确"}, msg="断言成功")
    
        # 密码为空
        def test_login_no_pwd(self):
            res = login.login_check(username="python27")
            self.assertEqual(res, {"code": 2, "msg": "所有的参数不能为空"}, msg="断言成功")
    
        # 用户名为空
        def test_login_no_usr(self):
            res = login.login_check(password="lemonban")
            self.assertEqual(res, {"code": 2, "msg": "所有的参数不能为空"}, msg="断言成功")
    
    
    if __name__ == "__main__":
        unittest.main()
    
    

    2. 测试用例前置处理与后置处理

    注意:前置与后置的处理,是需要定义在测试用例类中的,也就是继承了unittest.TestCase的类中、。

    1. setUp()前置方法

    在测试用例类中,定义setUp()方法,当每条测试用例执行的时候,都会先执行setUp()方法,这个方法表示测试用例执行前的前置准备工作,例如测试用例执行的时候需要先访问数据库,或者是创建浏览器实例等等。

    但是,一旦定义了setUp()方法,那么每条测试用例在执行的时候,都会进行相同的前置准备工作,不能够个性化 给每一条测试用例定义不同的前置准备。如果测试用例的前置不一样,那么需要单独重新定义一个新的测试类。

    class LoginTest(unittest.TestCase):
    
        # 每一条测试用例执行前,都会调用这个方法,这个是测试用例执行之前的前置准备工作
        def setUp(self):
            print("测试用例开始执行....")
        
        def test_login_cor(self):
            """这是一条测试用例"""
            pass
    
    2. tearDown()方法

    该方法类似于setUp()方法,只不过这个方法是在测试用例执行完毕之后,才会调用,就相当于每条测试用例执行之后的一个后置处理程序。就比如说要关闭浏览器实例,或者是断开与数据库的链接。

    class LoginTest(unittest.TestCase):
    
        # 每一条测试用例执行前,都会调用这个方法,这个是测试用例执行之前的前置准备工作
        def tearDown(self):
            print("测试用例开始执行....")
        
        def test_login_cor(self):
            """这是一条测试用例"""
            pass
    
    3. setUpClass()

    这个方法也是测试用例的一个前置条件,只不过有一点不同的是,它是一个类属性,只会在测试用例类开始执行的时候,调用一次,并不像setUp()方法是每条测试用例执行的时候都会调用。

    不管这个测试用例类中有几个测试用例,这个方法只会执行一次。

    由于这个方法是一个类属性,所以需要在方法前加装饰器@classmethod来修饰。

    与其相对应的就是tearDownClass()方法。

    class LoginTest(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            print("**** 测试用例类开始执行 ****")
        
        def test_login_cor(self):
            """这是一条测试用例"""
            pass
        
        def test_login_wrong(self):
            """这是一条测试用例"""
            pass
    
    4. tearDownClass()

    测试用例类的后置处理方法, 用于在整个测试用例执行完毕之后调用。

    class LoginTest(unittest.TestCase):
        
        @classmethod
        def setUpClass(cls):
            print("**** 测试用例类开始执行 ****")
        
        @classmethod
        def tearDownClass(cls):
            print("**** 测试用例类执行结束 ****")
        
        def test_login_cor(self):
            """这是一条测试用例"""
            pass
        
        def test_login_wrong(self):
            """这是一条测试用例"""
            pass
    

    3. 用例的收集:unittest.TestSuite()

    这个类,是unittest框架中专门用来收集编写好的各种测试用例的,它可以将测试用例全部收集起来,然后统一执行。

    将测试用例进行收集,这里有两种处理办法:addTest()addTests

    # 1.先将unittest框架和测试用例类引入进来
    import unittest
    
    from Homework.class_unittest_study import test_login
    
    # 实例化一个unittest.TestSuite()套件
    testSuite = unittest.TestSuite()
    
    # 调用addTest方法 或者是addTests()方法,将测试用例添加进来。
    testSuite.addTest(test_login.LoginTest("test_login_cor"))
    
    

    有一点需要注意:addTest()方法一次只能添加一个测试用例,addTests()一次能够添加多个测试用例。但是,添加的方式需要注意:需要在addTest()方法或者addTests()方法中,使用类名(用例方法名)的形式将测试用例添加进来。
    也就是如果你的测试用例类的名字是LoginTest,并且其中的一个用例的方法名是test_login_cor,那么就需要使用LoginTest("test_login_cor")的形式,将这个测试用例添加到测试套件中。

    4. 用例的收集:unittest.TestLoader()

    使用unittest.TestSuite()来收集测试用例,在实际的项目中很不方便,因为不论是addTest()还是addTests(),一旦碰上很多的测试用例,都会变得十分的繁琐,都需要去人工的一条一条的添加测试用例。

    所以,通常用来收集测试用例的,使用unittest.TestLoader()来收集测试用例。

    我们使用unittest.TestLoader()中的discover()来对指定项目路径下的文件,进行测试用例的收集工作。在discover()内部,为我们定义了测试用例的收集规则:

    1. 默认的从以test*.py文件中,获取测试类。也就是说,文件名是以test开头的文件,就是测试类所在的文件;
    2. 找到测试类所在的文件之后,再下一步就是从该文件中获取 测试用例:在测试类所在的文件中,从继承了unittest.TestCase的类中,找以test_开头的方法,认为以test_开头的方法就是测试用例。

    通过上两步的查找,会找到指定的路径中的全部测试用例,并把这些测试用例添加到实例化的unittest.TestLoader()对象中。

    # 1. 在discover方法中,指定搜索目录
    # 2. 在指定的搜索目录中,指定搜索文件
    # 3. 在指定搜索文件中,过滤测试用例:在继承了unittest.TestCase的测试类中,搜索以test_开头的方法,这就是测试用例。
    testLoader = unittest.TestLoader().discover(r"G:PythonTestHomeworkclass_unittest_study")
    print(type(testLoader))
    
    

    5. 运行测试用例集:unittest.TextTestRunner()

    这个是unittest框架自带的一个测试用例运行器,只需要调用就行了

    import unittest
    
    # 将运行结果写入文件
    with open("test_res.txt", mode="w", encoding="utf-8") as f:
        # 1. 实例化unittest.TextTestRunner()
        runner = unittest.TextTestRunner(f)
        # 2. 调用run方法,执行测试用例集
        runner.run(testLoader)
    

    但是,框架自带的这个运行测试用例的方法,它只会显示出一个txt文本格式的结果,在实际项目的时候使用的并不是很多。所以我们 使用别人开发好的一款运行测试用例并生成测试报告的类库:HTMLTestRunnerNew或者是BeautifulReport

    使用HTMLTestRunnerNew生成测试报告

    这个文件需要我们去网上手动下载,下载完成之后,添加到python的安装路径中的Lib下。

    这个类库生成的是一个HTML的文件,所以直接写入文件就可以了:

    import unittest
    
    from Homework.class_unittest_study import test_login
    
    testLoader = unittest.TestLoader().discover(r"G:PythonTestHomeworkclass_unittest_study")
    print(type(testLoader))
    
    from HTMLTestRunnerNew import HTMLTestRunner
    with open("reprot.html", mode="wb") as f:
        runner = HTMLTestRunner(f)
        runner.run(testLoader)
    

    这个类库会生成一个html格式的测试报告,里面会有默认的测试报告的标题,和测试人员,如果想要修改测试报告的名字或者是测试人员的名字,可以修改源码:

    DEFAULT_TITLE = 'py30单元测试报告'
    DEFAULT_DESCRIPTION = ''
    DEFAULT_TESTER='扈先生'
    

    也可以在实例化HTMLTestRunner对象的时候进行传参:

    runner = HTMLTestRunner(f, title="测试报告", tester="Tom")
    
    使用BeautifulReport生成测试报告

    使用pip下载包。

    import unittest
    
    from Homework.class_unittest_study import test_login
    
    testLoader = unittest.TestLoader().discover(r"G:PythonTestHomeworkclass_unittest_study")
    print(type(testLoader))
    
    from BeautifulReport import BeautifulReport
    br = BeautifulReport(testLoader)
    
    # 需要传入测试报告的标题,如果为None,则有默认的标题,也可以传入测试报告的文件路径,同样,如果不传,也有默认的文件名
    br.report("py30测试报告")
    

    BeautifulReport生成的测试报告,默认的是不会显示用例描述的。如果想要在测试报告中显示出来用例的描述,那么需要在测试函数中添加:self.__dict__['_testMethodDoc'] = "用例描述"

    并且,要把这句代码添加到测试函数中靠前的位置,最起码要在最后的断言前面,否则当永陵失败的时候,测试报告中这条失败的测试用例描述依然时空。

    6. 数据驱动思想执行测试用例:ddt框架

    在进行实际的自动化测试时,如果遇到单一流程,只是数据不同的测试时,单纯的使用unittest框架进行编写测试用例,再执行测试用例,这样会浪费大量的时间,严重影响效率。这个时候,就可以使用数据驱动测试的思想来实现测试工作。

    数据驱动测试,这是一种思维方式,也可以理解成将测试数据进行参数化,传递给相同的一个测试流程。

    python中,使用ddt模块来实现数据驱动测试的思维。

    ddt模块的使用很简单,都是一个固定的模板:

    1. 首先,引入ddt框架:import ddt
    2. 之后,在测试类前,添加装饰器@ddt.ddt
    3. 在测试函数前面,添加装饰器:@ddt.data(),并将测试数据传递给这个装饰器。这里最好使用一个列表来存储测试数据,然后使用拆包的方式传递参数;
    4. 并且在测试函数中,添加一个参数,来接收测试数据
    import unittest
    import ddt
    
    login_info = [
        {"username": "python27", "password": "lemonban", "except": {"code": 0, "msg": "登录成功"}},
        {"username": None, "password": None, "except": {"code": 2, "msg": "所有的参数不能为空"}},
        {"username": "python30", "password": "lemonban", "except": {"code": 1, "msg": "账号或密码不正确"}}
    ]
    
    @ddt.ddt
    class LoginTest(unittest.TestCase):
    
        @ddt.data(*login_info)
        def test_login_cor(self, case):
            res = login.login_check(case["username"], case["password"])
            self.assertEqual(res, case["except"], msg="断言失败")
    

    这样一来,ddt框架会根据你的测试数据,来自动为每一条测试数据生成一条测试用例,就上述例子来说,会直接生成3条测试用例。

    ddt的使用是一个固定的模板,十分简单。

    2. unittest框架中常用的断言类型

            方法                    检查
        assertEqual(a, b)           a == b      
        assertNotEqual(a, b)        a != b      
        assertTrue(x)               bool(x) is True      
        assertFalse(x)              bool(x) is False      
        assertIsNone(x)             x is None     
        assertIsNotNone(x)          x is not None   
        assertIn(a, b)              a in b    
        assertNotIn(a, b)           a not in b
    
  • 相关阅读:
    手机号码 正则表达式
    邮政编码的正则表达式
    对象为null,调用非静态方法产生空指针异常
    文件找不到异常(FileNotFoundException)
    数组下标越界异常解决方法
    空指针异常的解决方法
    需求:打印九九乘法表
    创建简单线程
    ·博客作业06--图
    博客作业05--查找
  • 原文地址:https://www.cnblogs.com/Pilaoban/p/13173051.html
Copyright © 2011-2022 走看看