• Python 进阶之单元测试框架 第13节课(单元测试 debug调试方法)


    单元测试

    1.什么是单元、单元测试

    单元: 指的是函数或者是类,测试的最小单元

    单元测试:就是测试代码里面的函数或者是类,是不是按照预先定义好的去执行

    2.为什么要做单元测试?

    好处:投入小,收益大,能够精准的、更早的发现问题

    3.单元测试与我有什么关系?

    Python语言很难测试java的单元

    单元测试一般是由开发,测开人员写的

    但是自动化测试可以做 集成测试、系统测试、验收测试

    4.学单元测试框架干嘛呢?(每一种语言都会自带一个单元测试框架)

    单元测试框架可以用于集成测试、系统测试

    5.编写被测对象(函数),后面根据这个函数再编写测试用例

    ♥单元测试框架:unittest

    ♥unittest的注意规范:模块名称  test_.....

              类名 Test....

              测试用例的方法名称  test_....

              测试用例类TestLogin(unittest.TestCase) 不要把继承的父类丢掉

    unittest 中,建立的 python file的名字必须是以test开头,比如:test_login.py

    (1)、编写测试登录功能(函数)

    """测试登录功能(函数)"""
    def login(username = None,password = None):
        """
        :param username: 登录校验的用户名
        :param password: 登录校验的密码
        :return:dict type
        """
        if username != None and password != None:
            if username == "polo" and password == "123456":
                return {"code":0,"msg":"登录成功!"}
            else:
                return {"code":1,"msg":"登录失败,用户名或密码错误!"}
        else:
            return {"code":1,"msg":"登录失败,用户名或密码为空!"}
    
    #用户名或密码为空
    if __name__ =='__main__':
        print(login())
    #密码错误
    if __name__ =='__main__':
        print(login('polo','123'))
    
    #用户名、密码都正确
    if __name__ =='__main__':
        print(login('polo','123456'))
    '''
    {'code': 1, 'msg': '登录失败,用户名或密码为空!'}
    {'code': 1, 'msg': '登录失败,用户名或密码错误!'}
    {'code': 0, 'msg': '登录成功!'}
    '''

     (2)、测试用例---TestCase (根据被测函数,设计测试用例)---------测试用例的方法是实例方法,不然用不了self.assertTrue

    测试用例的类名,必须是Test开头,比如:TestLogin

    断言:assertTrue(表达式)  重点用好这个,

               assertEqual(两个参数)--------assertEqual(4,3),不相等,报错

       assertGreater(两个参数)------assertGreater(4,3)4>3是正确的,就是等价于assertTrue(4>3)

    由上可知,assertTrue(表达式)能够包含别的用法,所以重点用好assertTrue(表达式)。熟悉了用法再去拓展别的用法。

    (assert断言的源码里已经进行了判断,用了try,用起来方便,比外面再进行if判断,写很多分支方便)

    ♥运行测试用例的注意事项

    ①写完代码换行,在空白行处运行

    ②运行测试用例,在setting→搜索unittest,将Default test runner 设置为Unittests

    举例1:两个测试用例都通过

    #导入单元测试框架unittest
    import unittest
    
    """测试登录功能(函数)"""
    def login(username = None,password = None):
        """
        :param username: 登录校验的用户名
        :param password: 登录校验的密码
        :return:dict type
        """
        if username != None and password != None:
            if username == "polo" and password == "123456":
                return {"code":0,"msg":"登录成功!"}
            else:
                return {"code":1,"msg":"登录失败,用户名或密码错误!"}
        else:
            return {"code":1,"msg":"登录失败,用户名或密码为空!"}
    
    #设计登录测试用例类
    class TestLogin(unittest.TestCase):#继承unittest.TestCase
    
        def test_login_success(self):
            '''登录成功'''
            username = "polo"
            password = "123456"
            expected_reponse = {"code":0,"msg":"登录成功!"}
    
            #实际结果:调用login函数
            actual_reponse = login(username,password)#login()函数是全局的,可以在类里面调用
            #判断预期结果跟实际结果是否一样   断言
            self.assertTrue(expected_reponse == actual_reponse)
    
    
        def test_login_error(self):
            '''登录失败'''
            username = ''
            password = ''
            expected_reponse = {"code":1,"msg":"登录失败,用户名或密码错误!"}
            actual_reponse = login(username, password)
            self.assertTrue(expected_reponse == actual_reponse)

     '''

      Ran 2 tests in 0.002s

      OK

      '''

     ♥上面的类直接运行测试用例,但是没有实例化对象------->因为unittest内部已经做了设置初始化,直接使用run 'unittest......'即可

    举例2:两个测试用例,有一个没通过(下面代码的黄底做了改动,预期结果跟实际结果不相等,异常)

    #导入单元测试框架unittest
    import unittest
    
    """测试登录功能(函数)"""
    def login(username = None,password = None):
        """
        :param username: 登录校验的用户名
        :param password: 登录校验的密码
        :return:dict type
        """
        if username != None and password != None:
            if username == "polo" and password == "123456":
                return {"code":0,"msg":"登录成功!"}
            else:
                return {"code":1,"msg":"登录失败,用户名或密码错误!"}
        else:
            return {"code":1,"msg":"登录失败,用户名或密码为空!"}
    
    #设计登录测试用例类
    class TestLogin(unittest.TestCase):#继承unittest.TestCase
    
        def test_login_success(self):
            '''登录成功'''
            username = "polo"
            password = "123456"
            expected_reponse = {"code":0,"msg":"登录成功....!"}
    
            #实际结果:调用login函数
            actual_reponse = login(username,password)#login()函数是全局的,可以在类里面调用
            #判断预期结果跟实际结果是否一样   断言
            self.assertTrue(expected_reponse == actual_reponse)
    
    
        def test_login_error(self):
            '''登录失败'''
            username = ''
            password = ''
            expected_reponse = {"code":1,"msg":"登录失败,用户名或密码错误!"}
            actual_reponse = login(username, password)
            self.assertTrue(expected_reponse == actual_reponse)

    结果:一个用例测试异常,不会影响下一个用例的执行。左边的Test Results 结果中可以查看异常的用例名称,右侧主要看该异常用例的错误类型

    须知:

    断言失败会抛出异常:AssertionError

    一个测试用例,写一个断言。

    多个用例之间互不影响:即上一个测试用例出现断言异常,不会影响下面的测试用例的执行(继续执行)

    unittest框架中设置好了初始化,在测试用例这个类里面不需要再进行初始化__init__了。

    (3).fixture----测试环境的搭建和销毁 (前置、后置条件)

    setUp() 、tearDown()是unitttest框架的固定写法,不能改。

      def setUp(self):
            '''前置条件'''
            pass
        
        def tearDown(self):
            '''后置条件'''
            pass

    ♥ ♥ 每执行一个测试用例,都会自动执行一遍setUp() 、tearDown(),举例:

    #导入单元测试框架unittest
    import unittest
    
    """测试登录功能(函数)"""
    def login(username = None,password = None):
        """
        :param username: 登录校验的用户名
        :param password: 登录校验的密码
        :return:dict type
        """
        if username != None and password != None:
            if username == "polo" and password == "123456":
                return {"code":0,"msg":"登录成功!"}
            else:
                return {"code":1,"msg":"登录失败,用户名或密码错误!"}
        else:
            return {"code":1,"msg":"登录失败,用户名或密码为空!"}
    
    #设计登录测试用例类
    class TestLogin(unittest.TestCase):#继承unittest.TestCase
    
        def setUp(self):
            '''前置条件'''
            print('连接数据库')
    
        def tearDown(self):
            '''后置条件'''
            print('断开数据库')
    
        def test_login_success(self):
            '''登录成功'''
            username = "polo"
            password = "123456"
            expected_reponse = {"code":0,"msg":"登录成功!"}
    
            #实际结果:调用login函数
            actual_reponse = login(username,password)#login()函数是全局的,可以在类里面调用
            #判断预期结果跟实际结果是否一样   断言
            self.assertTrue(expected_reponse == actual_reponse)
    
    
        def test_login_error(self):
            '''登录失败'''
            username = ''
            password = ''
            expected_reponse = {"code":1,"msg":"登录失败,用户名或密码错误!"}
            actual_reponse = login(username, password)
            self.assertTrue(expected_reponse == actual_reponse)

    结果如下:两个测试用例,前置、后置条件都会自动执行一遍

    ♥♥ 如果所有的测试用例,前置、后置条件都只执行一遍应该怎么办?------了解即可

    使用setUpclass(cls),一个测试类中,只会执行一次的前置条件(类方法)

    使用tearDownclass(cls),一个测试类中,只会执行一次的后置条件(类方法)

    #导入单元测试框架unittest
    import unittest
    
    """测试登录功能(函数)"""
    def login(username = None,password = None):
        """
        :param username: 登录校验的用户名
        :param password: 登录校验的密码
        :return:dict type
        """
        if username != None and password != None:
            if username == "polo" and password == "123456":
                return {"code":0,"msg":"登录成功!"}
            else:
                return {"code":1,"msg":"登录失败,用户名或密码错误!"}
        else:
            return {"code":1,"msg":"登录失败,用户名或密码为空!"}
    
    #设计登录测试用例类
    class TestLogin(unittest.TestCase):#继承unittest.TestCase
    
        @classmethod
        def setUpClass(cls):
            print('一个测试类中只会执行一次的前置条件')
    
        @classmethod
        def tearDownClass(cls):
            print('一个测试类中只会执行一次的后置条件')
    
        def setUp(self):
            '''前置条件'''
            print('连接数据库')
    
        def tearDown(self):
            '''后置条件'''
            print('断开数据库')
    
        def test_login_success(self):
            '''登录成功'''
            username = "polo"
            password = "123456"
            expected_reponse = {"code":0,"msg":"登录成功!"}
    
            #实际结果:调用login函数
            actual_reponse = login(username,password)#login()函数是全局的,可以在类里面调用
            #判断预期结果跟实际结果是否一样   断言
            self.assertTrue(expected_reponse == actual_reponse)
    
    
        def test_login_error(self):
            '''登录失败'''
            username = ''
            password = ''
            expected_reponse = {"code":1,"msg":"登录失败,用户名或密码错误!"}
            actual_reponse = login(username, password)
            self.assertTrue(expected_reponse == actual_reponse)

    '''

      一个测试类中只会执行一次的前置条件
      连接数据库
      断开数据库
      连接数据库
      断开数据库
      一个测试类中只会执行一次的后置条件

      '''

    注:运行代码是,右键-->run "unittest........"只有pycharm有这种功能

    其他的编辑器,可以在运行的代码下面顶格加如下:

    if __name__ == '__main__':
        #使用python去运行用例
        unittest.main()

    然后在terminal中用python运行:python lesson14 estcaseslogin.py

    (4)Test Suite------测试集/测试套件,多个测试用例集合在一起

    测试须知知识:

    ① unittest 中,测试用例的执行顺序

    不是从上到下来执行的,是根据测试用例的名称----→ASCII编码,美国标准编码规范

    测试用例名称的ASCII码的大小(前后顺序)来排列的

    上面例子的两个测试用例名称,success error, e 在s的前面,error的用例先执行.

    如果让success的用例先执行,error后执行,如下,加排序

    def test_login_01_success(self):

      pass

    def test_login_02_error(self):

      pass

     ②测试用例的执行方法

    1)右键→run"Unittests in .................",运行当前模块(只有pycharm有unittest 运行,其他的编辑器就要用方法2)去运行)

    2)增加 if __name__ == '__main__':    

          unittest.main()

    在终端里面,使用python,命令行运行当前这个模块里的测试用例.

    运行所有的测试用例怎么办???-----收集所有的用例 TestSuite&TestLoader()一起使用

    ① 测试用例的组织--会把测试用例的代码放到一个统一的文件夹中,目录当中tests / testcases

        方法:在项目录下new 一个directory,命名为tests / testcases,把所有的测试用例代码放在这个文件夹里面

    ②项目根目录,new一个python file(比如:run_tests.py,运行所有的程序),最好跟tests / testcases平级

         在这个模块里执行所有的程序,

     包括:收集所有的测试用例、执行、生成测试报告

    测试整个项目的时候,一般不会单独运行某个模块(调试的时候会运行单个模块)

     ③测试用例收集步骤

        1)将所有待测的测试用例放在,项目目录下new 一个directory,命名为tests / testcases里

      2)在run_test.py中,运行收集用例的代码

       1)). import unittest

       2)).初始化一个加载器    loader = unittest.TestLoader()

       3)).加载器去收集所有的测试用例(testcases里的所有用例),放在testsuite里面  test_suite = loader.discover(case_path)

        要获取testcases里的所有用例,就要获取该目录的绝对路径,该路径作为loader.discover()的参数(下面的例子testcases文件夹里有test_login.py文件,包含3个测试用例)

    import unittest
    import os
    
    #初始化一个加载器 test_loadder
    loader = unittest.TestLoader()
    
    #获取测试用例目录的路径,测试用例都放在tesetcases文件夹里
    dir_path  = os.path.dirname(os.path.abspath(__file__))
    case_path = os.path.join(dir_path,'testcases')
    
    #使用loader获取所有的测试用例
    test_suite = loader.discover(case_path)  #在case_path里面发现所有的测试用例
    print(test_suite)#就是返回一个testSuite对象,列表存储的

     测试用例收集完,怎么去执行测试用例呢?---------TextTestRunner()去执行测试集,用run方法

    步骤:1).初始化一个执行器runner

       2).runner.run(test_suite)  -----传入测试集合执行import unittest

    import os
    
    #初始化一个加载器 test_loadder
    loader = unittest.TestLoader()
    
    #获取测试用例目录的路径,测试用例都放在tesetcases文件夹里
    dir_path  = os.path.dirname(os.path.abspath(__file__))
    case_path = os.path.join(dir_path,'testcases')
    
    #使用loader获取所有的测试用例
    test_suite = loader.discover(case_path)  #在case_path里面发现所有的测试用例
    print(test_suite)#就是返回一个testSuite对象,列表存储的
    
    
    #执行测试用例
    #先初始化一个执行器runner
    runner = unittest.TextTestRunner()
    runner.run(test_suite)

    '''

    ...
    ----------------------------------------------------------------------
    Ran 3 tests in 0.000s

    
    

    OK
    <unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<unittest.suite.TestSuite tests=[<test_login.TestLogin testMethod=test_login_error>, <test_login.TestLogin testMethod=test_login_pwderror>, <test_login.TestLogin testMethod=test_login_success>]>]>, <unittest.suite.TestSuite tests=[]>]>
    连接数据库
    断开数据库
    连接数据库
    断开数据库
    连接数据库
    断开数据库

    '''

    上面的运行结果,没有测试报告。

    如何去生成一个测试报告呢?-------with open('test_report.txt') as f:

    import unittest
    import os
    
    #初始化一个加载器 test_loadder
    loader = unittest.TestLoader()
    
    #获取测试用例目录的路径,测试用例都放在tesetcases文件夹里
    dir_path  = os.path.dirname(os.path.abspath(__file__))
    case_path = os.path.join(dir_path,'testcases')
    
    #使用loader获取所有的测试用例
    test_suite = loader.discover(case_path)  #在case_path里面发现所有的测试用例
    print(test_suite)#就是返回一个testSuite对象,列表存储的
    
    
    #执行 并生成text测试报告
    with open('test_report.txt','a',encoding='utf-8') as f:
        runner  = unittest.TextTestRunner(f) #f作为参数传入
        runner.run(test_suite)

    执行完,在run_test.py的同级目录下生成一个test_report.txt文件,记录结果信息

    下图,将其中一个测试用例的数据修改,让其运行在断言的地方报错

     报告结果中显示:..F (3个测试用例)   点 表示通过,F表示failure(测试用例的问题:①测试用例的预期结果不对 ②执行中确实存在bug),E 表示error,代码本身存在问题(添加1/0去运行,就会报错E)

    但是在实际中,不会使用txt形式的报告,比较low,会换用HTML的报告.

    HTML报告不是内置的,需要自己去下载HTML的测试报告的运行器和模板(HTMLTestRunnerNew.py),导入到pycharm的seite_package 或者直接导入项目目录下.

     首先看一下HTMLTestRunner的源码

    class HTMLTestRunner(Template_mixin):
        """
        """
        def __init__(self, stream=sys.stdout, verbosity=2,title=None,description=None,tester=None):

    执行 生成HTML报告

    #执行 并生成html测试报告
    with open('test_report.html','wb') as f:
        runner = HTMLTestRunner(
            f,
            title='python29期第一次自动化测试报告',
            description='测试报告的描述',
            tester='polo'
    
        )
        runner.run(test_suite)

    结果:

     总结:运行用例的流程

    --------1.编写用例方法,用例方法放到一个目录中

    --------2.写脚本run.py  收集用例  运行用例  loader收集用例 suite = discover()

    --------3.得到测试集 test_suite

    --------4.运行,test_runner = HTMLTestRunner

    拓展知识(了解,用的比较少):不使用discover,只想运行某2个用例,不运行所有的用例

    课程-数据分离结合excel实际应用(在25min左右时间段讲解)

     from   XXX import test_login,test_register

     最后运行:runner.run(suit_total)

    注:运行哪些用例,将用例add起来,是用list表示的,表示一个可迭代的对象。

    (5) debug 设置断点  ------运行用debug  run

    ①断点打在哪里?

       ♥知道会报错的那行,打断点,程序运行到这一行就会停止(该行不会运行)

       ♥如果不知道哪里会报错,就打在程序最开始的地方,再一步一步调试

    ②断点调试,pycharm中调式的类型

     ♥1)--------step over(F8)   ,表示单步执行

    ♥2)-------------step  into(F7),表示进入函数内

    举例,在下面这行代码设置断点,运行后,执行step into ,就会进入login()函数中去

    actual_response = login(username,password)

    进入login()函数中以后,可以使用step into,或者step over.

     3)-----------step into my code ,只能进入我自己写的代码,别人写的进不去,一般很少用,用step into比较多

    4)-------------step out 退出函数

    ♥5)-------------run to Cursr 表示运行到光标的位置, 指定的行. 即打了断点以后,将光标放在想要运行指定的哪行代码上,点击run to Cursr ,就会运行指定的这行代码

    6)------------evaluate expression 计算器 

    可以在进行调试的时候,对数据进行某些处理,查看结果

     7)--------rerun  重新运行,点击会再次运行程序

    8)--------resume program  运行到下一个断点(中间不会停顿)

  • 相关阅读:
    (转载)教你在PHP中使用全局变量
    (转载)遍历memcache中已缓存的key
    (转载)PHP_Memcache函数详解
    PHP去除空白字符
    (转载)用PHP正则表达式清除字符串的空白
    (转载)PHP静态方法
    (转载)PHP 动态生成表格
    (转载)PHP strtotime函数详解
    (转载)URL与URI的区别
    ldap集成confluence
  • 原文地址:https://www.cnblogs.com/ananmy/p/12939423.html
Copyright © 2020-2023  润新知