• Django测试pytest


    一、概述

    1. 课程概述

    2. 课程安排

     

    二、接口测试

    1.【知道】什么是接口测试

    image-20200905091838904

     

    2. 环境准备

    • 创建虚拟环境

    • 下载模块

    • 运行项目代码

     

    3. Postman的基本使用

    3.1【掌握】借助Postman完成接口测试

    json格式数据如何发送

    image-20200905094914244

    3.2【掌握】导出导入Postman测试记录

    4. Requests的使用

    • Requests是一个功能强大的HTTP Client,能够帮助我们发送HTTP请求,并获取响应

    4.1 发送GET请求

    4.1.1【掌握】发送GET请求
    import requests

    url = 'http://127.0.0.1:8080/user'

    # 响应对象 = requests.get(url)
    resp = requests.get(url)
    print(resp.text)
    4.1.2【掌握】响应状态码、响应头、响应体
    # 响应状态码、响应头、响应体
    print(resp.status_code) # 401
    print(resp.headers) # 响应头
    print(resp.text, type(resp.text)) # {"error": "用户未登录"} <class 'str'>

    print(resp.content) # 二进制

    print(resp.json()) # 是字典类型,如果返回的格式不是json,发生异常, JSONDecodeError
    4.1.3【知道】字符编码
    # 查看编码
    print(resp.encoding) # ISO-8859-1

    # 显示中文,修改编码格式为utf-8
    resp.encoding = 'utf-8'
    4.1.4【掌握】resp.json
    print(resp.json()) # 是字典类型,如果返回的格式不是json,发生异常, JSONDecodeError

     

    4.1.5【知道】构造请求头和cookies
    # 构造header
    _headers = {'User-Agent':'hello mike'}
    resp = requests.get(url, headers=_headers)
    print(resp.text) # {"User-Agent": "hello mike", "Cookies": {}}

    _cookies = {
       'k1':'v1',
       'k2':'v2'
    }

    resp = requests.get(url, headers=_headers, cookies=_cookies)
    print(resp.text)

    4.2【掌握】发送POST请求

    import requests

    # 如果以表单格式提交数据,使用data关键字参数
    # 如果以json格式提交数据,使用json关键字参数

    # 构建用户登陆数据
    user_info = {
       'username':'mike123',
       'password':'12345678'
    }

    # 发送Post请求
    resp = requests.post('http://127.0.0.1:8080/login', data=user_info)

    # 获取响应内容
    print(resp.json())

     

    4.3【掌握】requests和requests.session的区别

    • requests不会状态保持

    • requests.session可以状态保持

    import requests

    # 实例化一个 requests.Session() 对象
    # session对象发送数据,发送请求方式和requests一样

    session = requests.Session()

    # 登陆
    login_url = 'http://127.0.0.1:8080/login'

    # 构建用户登陆数据
    user_info = {
       'username':'mike123',
       'password':'12345678'
    }
    resp = session.post(login_url, data=user_info)
    print(resp.json())


    # 获取登陆用户的信息
    resp = session.get("http://127.0.0.1:8080/user")
    print(resp.json())

     

    三、基于django的单元测试

    1.【知道】认识单元测试

    • 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

    • 单元测试侧重于代码逻辑,接口测试侧重于业务逻辑

    2.【掌握】编写和运行django的单元测试

    • 写的代码集成在项目代码中,创建test开头的文件

    • 类继承django.test.TestCase

      from django.test import  TestCase

      class MyTestCase(TestCase):
         # 1. 前置、后置方法,名字固定
         def setUp(self) -> None:
             print('前置setUp2')

         def tearDown(self) -> None:
             print('后置tearDown2')

         # 2. 测试用例,test开头的方法
         def test_1(self):
             print('测试用例12')
             
         def test_2(self):
             print('测试用例22')
    • 命令运行

      # 执行项目中全部的测试用例,项目中所有以test开头的py文件都会执行
      python manage.py test 
      
      # 运行某个名为users的app下面的所有的测试用例,users是应用的名字(APP_name),应用中所有以test开头的py文件都会执行
      python manage.py test users 
      
      # 运行某个app下面的tests.py文件
      python manage.py test users.tests 
      
      # 运行某个app下面的tests.py文件中指定的class类
      python manage.py test users.tests.MyClassTestCase
      
      # 执行ModeTest类下的某个测试方法
      python manage.py test users.tests.MyClassTestCase.test_my_func

     

    3. TestCase类

    3.1【知道】前后置方法运行特点

    • 类方法的前后置方法:在类启动、结束调用

    • 实例前后置方法:test开头的实例方法前后调用

    from django.test import TestCase
    
    class MyTestCase(TestCase):
        # 1. 前置、后置方法,名字固定
        # 针对于test开头的实例方法
        def setUp(self) -> None:
            print('前置setUp
    ')
    
        def tearDown(self) -> None:
            print('后置tearDown
    ')
    
        @classmethod
        def setUpClass(cls):
            print('类方法setUpClass')
    
        @classmethod
        def tearDownClass(cls):
            print('类方法tearDownClass')
    
        # 2. 测试用例,test开头的方法
        def test_1(self):
            print('测试用例1')
    
        def test_2(self):
            print('测试用例2')

     

    3.2【掌握】setUpClass 和 tearDownClass应用场景

    # 定义 setUpClass: 用户登录
    # 定义 tearDownClass: 用户退出
    # 定义测试方法:获取登陆用户、查看UA和Cookie
    
    from django.test import TestCase
    from requests import Session
    
    class DjangoTest(TestCase):
        # 定义 setUpClass: 用户登录
        @classmethod
        def setUpClass(cls):
            # 实例化对象, cls.session类属性
            cls.session = Session()
    
            # 登陆
            login_url = 'http://127.0.0.1:8080/login'
    
            # 构建用户登陆数据
            user_info = {
                'username': 'mike123',
                'password': '12345678'
            }
            resp = cls.session.post(login_url, data=user_info)
            print("登陆", resp.json())
    
        # 定义 tearDownClass: 用户退出
        @classmethod
        def tearDownClass(cls):
            resp = cls.session.get('http://127.0.0.1:8080/logout')
            print("登出", resp.json())
    
    
        # 查看用户信息
        def test_user(self):
            resp = self.session.get('http://127.0.0.1:8080/user')
            print("用户信息", resp.json())
    
        # 查看cookies
        def test_header(self):
            resp = self.session.get('http://127.0.0.1:8080/header')
            print("头部信息", resp.json())

     

    4. Client类

    4.1 Client介绍

    • 使用和requests.session一样

    • 可以状态保持

    • 不用启动服务也可以使用,数据不会保存在数据库,放在临时的数据库

     

    4.2【知道】Client的使用

    • 虽然不用启动后台服务,但是依赖于后台代码,所以,接口协议按照后台的规定

    • 由于没有启动服务,所以,不需要url

    • 其它用法和requests.Session用法一样

    # 注册
    # 登录
    # 查看UA和Cookie
    # 登出
    # 再次查看UA和Cookie
    
    from django.test import TestCase, Client
    
    class DjangoTest(TestCase):
        def setUp(self) -> None:
            print('setUp')
            # 共享实例属性的数据
            self.user_info = {
                'username': 'mike123',
                'password': '12345678',
                'mobile': '13344445555'
            }
    
            # 实例化client对象
            self.client = Client()
    
    
        def test_api(self):
            print('注册、登陆、查看、退出、查看')
            self.do_register()
            self.do_login()
            self.do_header()
            self.do_logout()
            self.do_header()
    
    
        def do_register(self):
            print('注册')
            resp = self.client.post('/register', data=self.user_info)
            print(resp.json())
    
        def do_login(self):
            print('登陆')
            resp = self.client.post('/login', data=self.user_info)
            print(resp.json())
    
        def do_header(self):
            print('查看')
            resp = self.client.get('/header')
            print(resp.json())
    
        def do_logout(self):
            print('登出')
            resp = self.client.get('/logout')
            print(resp.json())

     

    4.3 测试django自带的用户验证系统

        def do_login(self):
            print('登陆')
            # 项目使用django自带的用户验证系统,可用login函数和logout函数进行测试
            resp = self.client.login(username=self.user_info['username'],
                              password=self.user_info['password'])
            print(resp)
    
        def do_logout(self):
            print('登出')
            self.client.logout()

     

    5. 断言

    5.1【知道】断言介绍

    • 测试结果是否符合预期,如果符合,说明测试通过(断言成功),如果不符合测试失败(断言失败)

          # 检查是否登录成功
            if result_code != 200:
                # 只要状态码不是200,说明登陆失败,不符合预期,抛出断言异常
                raise AssertionError(f"实际的结果是{result_code},预期的结果是:200")

     

    5.2【掌握】断言的使用

            # 如果result_code和200相同,说明符合预期,测试通过,msg不会显示
            # 否则,测试不通过,msg显示
            self.assertEqual(result_code, 200, msg='状态玛不为200,测试不通过')

     

     

     

     

    1. Pytest介绍

    • 默认自动识别当天目录下test开头的函数和方法、Test开头的类

    2.【掌握】Pytest 第一个例子

    import pytest
    import requests

    # 定义2个test开头的函数
    def test_one():
       print('test_one')

    def test_two():
       print('test_two')

    # 定义一个Test开头的类
    class TestClass(object):
       # 定义2个test开头的实例方法
       def test_1(self):
           print('11111111111111111111111')

       def test_2(self):
           print('222222222222222')

    if __name__ == '__main__':
       # pytest.main() # 默认允许同一个目录下,test开头的文件,默认不支持打印,需要配置才能打印
       # pytest.main(['-s']) # -s, 可以打印
       pytest.main(['-s', 'test_1.py'])
    • pytest自动失败test开头的函数和方法,或者Test开头的类

      pytest.main()   # 不打印运行同目录所有test文件
      pytest.main(['-s']) # 可以打印
      pytest.main(['-s', 'test_1.py']) # 只运行test_1.py文件
    • 命令行运行

    • pytest

      • 会执行当前文件的同时,路径下所有以test开头的py文件都会被执行

        • pytest先获取所有文件中的测试函数后,再执行

      • 不会显示代码中的标准输出(print、logging)

    • pytest -s

      • 会执行当前文件的同时,路径下所有以test开头的py文件都会被执行

        • pytest先获取所有文件中的测试函数后,再执行

      • 显示代码中的标准输出(print、logging)

    • pytest test_1.py

      • 只会执行当前文件

      • 不会显示代码中的标准输出(print、logging)

    • pytest -s test_1.py

      • 只会执行当前文件

      • 显示代码中的标准输出(print、logging)

     

    3. 断言的处理

    3.1【掌握】assert断言语句

        # django自带断言 resp.status_code, 200, msg=‘提示信息’
       # assert 条件表达式, ’提示信息‘
       assert resp.status_code == 200, '测试不通过,状态码不是200'

     

    3.2 异常断言

    3.2.1【掌握】pytest.raises()的使用

    """
    # 产生的异常和预期要捕获到的异常一样,则测试通过
    with pytest.raises(预期要捕获到的异常):
      产生的异常
    """

    class TestAssert(object):
       def test_assert(self):
           with pytest.raises(TypeError):
               is_leap_year("2020")

     

    3.2.2【知道】把异常信息存储到变量

        def test_assert2(self):
           with pytest.raises(TypeError) as err_info:
               is_leap_year("2020")
             
    err_info.__dict__ # 查看对象内部成员信息
           assert err_info.type==TypeError, '捕获到的异常不匹配'

     

    3.2.3 通过异常的内容捕捉异常

            # 通过异常提示信息捕获指定异常
           with pytest.raises(BaseException, match='不是整数') as err_info:
               is_leap_year("2020")
               
           if err_info.type == TypeError:
               print('aaaaaaaaaaa')

     

    3.3 警告断言

    • 捕获警告,捕获到警告,测试通过

    • 可以同时捕获多个警告

     

    3.3.1【掌握】pytest.warns()的使用

    3.3.2【知道】把警告信息存储到变量

    class TestWarn(object):
       def test_1(self):
           with pytest.warns(UserWarning):
               warnings.warn("value must be 0 or None", UserWarning)

       def test_2(self):
           # 由于捕获了2个警告,warn_info就是列表
           with pytest.warns((UserWarning, RuntimeWarning)) as warn_info:
               warnings.warn("value must be 0 or None", UserWarning)
               warnings.warn("value must11111", RuntimeWarning)

           # print(warn_info.__dict__)
           print(len(warn_info))
           # {'message': UserWarning('value must be 0 or None',),
           # 'category': <class 'UserWarning'>, 'filename': '/home/python/code/pytest_code/test_4_warn.py', 'lineno': 25, 'file': None, 'line': None, 'source': None, '_category_name': 'UserWarning'}
           print(warn_info[0].__dict__)
           print(warn_info[0].message)
           print(warn_info[0].category)

    3.3.3 通过警告的内容捕捉警告

    >> > with warns(UserWarning, match='must be 0 or None'):
      ...
      warnings.warn("value must be 0 or None", UserWarning)

    >> > with warns(UserWarning, match=r'must be d+$'):
      ...
      warnings.warn("value must be 42", UserWarning)

    >> > with warns(UserWarning, match=r'must be d+$'):
      ...
      warnings.warn("this is not here", UserWarning)

     

    4. setup和teardown函数

    4.1【知道】函数级别

    # 函数级别的前、后置函数,只针对普通测试用例
    
    def setup_function():
        print('setup_function')
    
    def teardown_function():
        print('teardown_function')
    
    def test_1():
        print('普通测试用例111111')
    
    def test_2():
        print('普通测试用例111111')
    
    class TestClass(object):
        def test_one(self):
            print('类实例方法测试用例111111')
    

    4.2【知道】类级别

    class TestClass(object):
        # 针对类的前置后置方法
        @classmethod
        def setup_class(cls):
            print('setup_class')
    
        @classmethod
        def teardown_class(cls):
            print('teardown_class')
    
        # 针对测试实例方法
        def setup_method(self):
            print('setup_method')
    
        def teardown_method(self):
            print('setup_method')
    
        def test_one(self):
            print('类实例方法测试用例111111')
    
        def test_two(self):
            print('类实例方法测试用例22222')
    • 类的前置后置方法:在类启动、结束运行

    • 测试实例方法:类中测试实例方法调用前、调用结束运行

     

    4.3【知道】模块级别

    # 针对模块
    def setup_module():
        print('setup_module')
    
    def teardown_module():
        print('teardown_module')
    
    
    # 针对普通测试函数
    def setup_function():
        print('setup_function')
    
    def teardown_function():
        print('teardown_function')
    
    def test_1():
        print('普通测试用例111111')
    
    def test_2():
        print('普通测试用例22222')
    
    class TestClass(object):
        # 针对类的前置后置方法
        @classmethod
        def setup_class(cls):
            print('setup_class')
    
        @classmethod
        def teardown_class(cls):
            print('teardown_class')
    
        # 针对测试实例方法
        def setup_method(self):
            print('setup_method')
    
        def teardown_method(self):
            print('teardown_method')
    
        def test_one(self):
            print('类实例方法测试用例111111')
    
        def test_two(self):
            print('类实例方法测试用例22222')

    运行结果:

    test_8.py setup_module
    setup_function
    普通测试用例111111
    .teardown_function
    setup_function
    普通测试用例22222
    .teardown_function
    setup_class
    setup_method
    类实例方法测试用例111111
    .teardown_method
    setup_method
    类实例方法测试用例22222
    .teardown_method
    teardown_class
    teardown_module

     

    5. pytest.fixture基本使用

    5.1【理解】fixture基本使用

    image-20200906110709798

    # 0.@pytest.fixture装饰函数
    @pytest.fixture()
    def get_web_url():
        print('get_web_url')
        return 'http://www.baidu.com'
    
    # 1. 把上面函数名作为测试用例的参数
    def test_web(get_web_url):
        # 2. 测试用例调用前,需要先确定形参get_web_url,就是调用get_web_url
        print('test_web')
        print(get_web_url) # 测试用例内部使用get_web_url,就是使用它返回值
        r = requests.get(get_web_url)
        assert r.status_code == 200, '测试失败'

     

    5.2【掌握】fixture装饰的函数

    @pytest.fixture()
    def foo():
        print('
    foo: 1111111111111')
        return 'http://itcast.cn'
    
    @pytest.fixture()
    def bar(foo):
        resp = requests.get(foo)
        print('bar: 22222222222222')
        return resp
    
    def test_web(bar):
        print('333333333333')
        # 响应状态码应该是200, 否则会断言失败
        assert bar.status_code == 200
    
    if __name__ == '__main__':
        pytest.main(['-s', 'test_10.py'])

     

    5.3 conftest.py文件

    5.3.1【掌握】共享fixture函数

    image-20200906114706776

     

    5.3.2【掌握】pytest.mark.usefixtures

    image-20200906115247154

     

    6. pytest.fixture参数

    6.1【知道】scope参数

    • function:设置为function,表示每个测试方法都要执行一次, 普通测试用例、类里面的测试用例都可以执行

    • class

    # 普通测试方法,正常执行,类里面的测试实例方法,只会执行1一次
    import pytest
    
    @pytest.fixture(scope='class')
    def foo():
        print('foo')
    
    
    def test_1(foo):
        print('普通测试用例111111')
    
    def test_2(foo):
        print('普通测试用例22222')
    
    
    class TestClass(object):
        def test_one(self, foo):
            print('类实例方法测试用例111111')
    
        def test_two(self, foo):
            print('类实例方法测试用例22222')
    # module:只会在传参的时候执行1次,针对test开头的文件
    # session:只会在session开始的时候执行1次,在一个文件夹启动所有测试文件,session是整个文件夹
    import pytest
    
    # @pytest.fixture(scope='module')
    @pytest.fixture(scope='session')
    def foo():
        print('foo')
    
    
    def test_1(foo):
        print('普通测试用例111111')
    
    def test_2(foo):
        print('普通测试用例22222')
    
    
    class TestClass(object):
        def test_one(self, foo):
            print('类实例方法测试用例111111')
    
        def test_two(self, foo):
            print('类实例方法测试用例22222')

     

    6.2 params参数

    6.2.1【理解】案例1

    import pytest
    
    def check_password(password):
        """
        检查密码是否合法
    
        :param password: 长度是 8 到 16
        :return:
        """
        pwd_len = len(password)
        if pwd_len < 8:
            return False
        elif pwd_len > 16:
            return False
        else:
            return True
    
    # 参数化
    @pytest.fixture(params=['1234', '1312532532', '1325215', '135325'])
    def get_pwd(request): # request固定
        return request.param
    
    
    # 测试用例
    def test_pwd_len(get_pwd):
        print(get_pwd)
        print(check_password(get_pwd))

     

    6.2.2【理解】案例2

    import pytest
    
    # 参数化
    @pytest.fixture(params=['mike123', 'mike666', 'admin12'])
    def get_user(request): # request固定
        return request.param
    
    @pytest.fixture(params=['12345678', 'mike66611', 'admin1211'])
    def get_pwd(request): # request固定
        return request.param
    
    
    # 测试用例
    def test_login(get_user, get_pwd):
        print(get_user, ' ===> ', get_pwd)
    
    test_15.py mike123  ===>  12345678
    .mike123  ===>  mike66611
    .mike123  ===>  admin1211
    .mike666  ===>  12345678
    .mike666  ===>  mike66611
    .mike666  ===>  admin1211
    .admin12  ===>  12345678
    .admin12  ===>  mike66611
    .admin12  ===>  admin1211

     

    6.3【知道】autouse参数

    import pytest
    
    # autouse=True, 当前所有测试用例前调用一次
    @pytest.fixture(autouse=True)
    def foo():
        print('foo')
    
    
    def test_1():
        print('普通测试用例111111')
    
    def test_2():
        print('普通测试用例22222')
    
    
    class TestClass(object):
        def test_one(self):
            print('类实例方法测试用例111111')
    
        def test_two(self):
            print('类实例方法测试用例22222')

     

    7. pytest.mark标记

    7.1【知道】pytest.mark.xfail()

    # 写了condition后,需要写上reason
    # @pytest.mark.xfail(condition=True, reason='标志失效')
    # @pytest.mark.xfail(condition=False, reason='标志失效') # False,相当于没有写这句话
    # @pytest.mark.xfail(condition=True, reason='标志失效', raises=TypeError)
    @pytest.mark.xfail(reason='标志失效')
    def test_1():
        print('
    普通测试用例111111
    ')

     

    7.2【知道】pytest.mark.skip()

    7.3【知道】pytest.mark.skipif()

    # 修饰的测试用例,跳过,不执行
    @pytest.mark.skip(reason='跳过,不执行')
    def test_1():
        print('
    普通测试用例111111
    ')
    
    def test_2():
        print('
    普通测试用例22222
    ')
    
    # True跳过,False执行
    @pytest.mark.skipif(condition=False,reason='跳过,不执行')
    def test_3():
        print('
    普通测试用例333333
    ')

     

    7.4【知道】pytest.mark.parametrize()

    import pytest
    
    
    @pytest.mark.parametrize('num', [1, 2, 3, 4])
    def test_get_num(num):
        print(num)
    
    @pytest.mark.parametrize(['user', 'pwd'],[('mike123', '12345678'), ('hello11', 'hdshghadshg'), ('aaaaa', 'aaaaaddd')])
    def test_login(user, pwd):
        print(user, pwd)
    test_19.py 1
    .2
    .3
    .4
    .mike123 12345678
    .hello11 hdshghadshg
    .aaaaa aaaaaddd









    一、pytest(下)

    1. pytest配置文件

    1.1【知道】配置pytest命令行运行参数

    • addopts:指定运行的文件,如果配置文件指定了运行的文件,代码中不建议指定运行文件

    • 如果目录中嵌套背的代码目录,这个目录建议新建一个__init__.py文件

    [pytest]
    ;addopts = -s test_18.py test_19.py ;注释
    addopts = -s

     

    1.2【知道】配置文件参数详解

    [pytest]
    ;addopts = -s test_18.py test_19.py ;注释,占一行
    addopts = -s
    ;识别目录 tests
    testpaths = ./tests
    python_files = test*.py
    python_classes = Test*
    python_functions = test*

    具体参数解读

    • addopts - 配置pytest命令行运行参数

      • 空格分隔,可添加多个命令行参数

    • testpaths - 配置测试搜索的路径

      • 当前目录下的测试脚本文件夹 可自定义

    • python_files - 配置测试搜索的文件名

      • 当前目录下的测试脚本文件夹下,以test_开头,以.py结尾的所有文件 可自定义

    • python_classes - 配置测试搜索的测试类名

      • 当前目录下的测试脚本文件夹下,以Test_开头的类 可自定义

    • 配置测试搜索的测试函数名

      • 当前目录下的测试脚本文件夹下,以test_开头的⽅法 可自定义

     

    2. pytest常用插件

    2.1【掌握】生成测试报告

    • 需要先按照插件:pip install pytest-html # 生成测试报告

    • 运行命令:pytest -s 测试用例文件 --html=./报告名字.html

     

    2.2【知道】控制函数执行顺序插件

    • 安装插件:pip install pytest-ordering

    • order=1, 数字小的先运行

    import pytest

    class Test_ABC:
       def setup_class(self):
           print("setup_class")
       def teardown_class(self):
           print("teardown_class")

       @pytest.mark.run(order=2) # order=2 后运行
       def test_a(self):
           print("test_a")
           assert 1

       @pytest.mark.run(order=1) # order=1 先运行
       def test_b(self):
           print("test_b")
           assert 1

       @pytest.mark.run(order=3) # order=3 先运行
       def test_c(self):
           print("test_c")
           assert 1

     

    2.3 失败重试

    • pip install pytest-rerunfailures

    • 命令:--reruns 重来次数

    • 失败重试,重新开始,只重试测试用例失败的例子

     

    2.4 按名称取消插件

    • -p no:ordering -p no:html -p no:rerunfailures

       

    3. Yaml

    3.1【知道】Yaml语法格式

    image-20200908100652543

    3.2 Python处理yaml文件

    • pip3 install -U PyYAML

    3.2.1【掌握】读取yaml文件

    import yaml

    def test_read():
       # 1. 打开文件, 文件对象
       # 2. 返回数据 = yaml.load(文件对象)
       with open('./xxx.yaml', 'r') as f:
          data = yaml.load(f, Loader=yaml.FullLoader)
          print(data)

     

    3.2.2【掌握】写入文件内容

    import yaml


    data={'Search_Data': {
             'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'},
             'search_test_001': {'expect': [4, 5, 6], 'value': 456}
                          }
    }

    # 1. 只写方式打开文件,制定utf-8编码,返回文件对象
    # 2. yaml.dump(数据,文件对象, allow_unicode=True)

    def test_write():
       with open('yyy.yaml', 'w', encoding='utf-8') as f:
           yaml.dump(data, f, allow_unicode = True)

     

    4. 综合案例

    4.1【理解】测试目录结构

    测试目录结构:

    数据准备:

    pytest.ini:

    [pytest]
    addopts = -s --html=report/report.html
    testpaths = ./tests
    python_files = test_*.py
    python_classes = Test*
    python_functions = test_*

    server.yaml:

    schema: http
    host: localhost
    port: 8080

    register.ymal:

    url: '/register'
    user1:
      username: 'admin11'
      password: '12345678'
      mobile: '13344445555'
    user2:
      username: 'admin22'
      password: '12345678'
      mobile: '13344445555'
    user3:
      username: 'admin33'
      password: '12345678'
      mobile: '13344445555'

    login.yaml:

    url: '/login'
    user1:
        username: 'admin11'
        password: '12345678'
    user2:
        username: 'admin22'
        password: '12345678'
    user3:
        username: 'admin33'
        password: '12345678'

    login_logout.yaml:

    url: '/login'
    user1:
        username: 'admin11'
        password: '12345678'

    4.2【掌握】公用方法类:读取yaml数据

    # 1. 封装接口,传入文件名,自动返回读取到的文件信息
    # 2. 内部处理路径
    import os
    import yaml
    
    class Data(object):
        # 类属性,获取project的绝对路径
        # os.path.dirname()返回去掉文件名后返回的绝对路径
        # os.path.abspath(__file__) 当前文件的绝对路径
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
        @classmethod
        def read_yaml(cls, file_name):
            # 拼接路径  /home/python/code/pytest_project
            file_path = os.path.join(cls.BASE_DIR, 'data', file_name)
            print(file_path)
            # 打开文件
            with open(file_path, 'r') as f:
                # 读取内容
                data = yaml.load(f,  Loader=yaml.FullLoader)
    
            return data
    
    
    # print(Data.BASE_DIR)
    # print(Data.read_yaml('server.yaml'))
    # print(Data.read_yaml('users/register.yaml'))

     

    4.3【掌握】公用方法类:请求封装

    """
    1. 获取服务器的信息,读取文件,拆包
    2. 封装get请求,post请求
    """
    # 从哪里运行,从哪里开始导包
    from pytest_project.utils.data import Data
    import requests
    
    class Ask(object):
        @staticmethod
        def get_url():
            """http://locast:8080"""
            # { schema: 'http', host: 'localhost', port: 8080 }
            server = Data.read_yaml('server.yaml')
            return f"{server['schema']}://{server['host']}:{server['port']}"
    
    
        @classmethod
        def get(cls, path):
            """
            :param path: 路径参数, /register
            :return: 响应对象
            """
            resp = requests.get(cls.get_url()+path)
            return resp
    
        @classmethod
        def post(cls, path, data=None, json=None):
            """
            :param path: 路径参数, /register
            :return: 响应对象
            """
            resp = requests.post(cls.get_url()+path, data=data, json=json)
            return resp

     

    4.4【掌握】注册单元测试用例

    import pytest
    from pytest_project.utils.ask import Ask
    from pytest_project.utils.data import Data
    
    """
    1. 读取配置文件的信息,提取用户信息
    2. 组包 [(url, {注册信息}), (url, {注册信息}), (url, {注册信息})]
    [('/register', {注册信息1}), ('/register', {注册信息2}), ('/register', {注册信息3})]
    """
    
    def get_regiter_info():
        users_info = Data.read_user_yaml('register.yaml')
        # print(users_info)
        args_list = []
        for k, v in users_info.items():
            if k != 'url':
                args_list.append((users_info['url'], v))
    
        # print(args_list)
        return args_list
    
    
    class TestUsers(object):
        @pytest.mark.skipif(condition=True, reason='注册跳过哦')
        @pytest.mark.parametrize(['url', 'user_info'], get_regiter_info())
        def test_register(self, url, user_info):
            resp = Ask.post(url, data=user_info)
            print(resp.json())
            assert resp.status_code == 200, '注册失败'

     

    4.5【掌握】登陆单元测试用例

    import pytest
    from pytest_project.utils.ask import Ask
    from pytest_project.utils.data import Data
    
    """
    1. 读取配置文件的信息,提取用户信息
    2. 组包 [(url, {注册信息}), (url, {注册信息}), (url, {注册信息})]
    [('/register', {注册信息1}), ('/register', {注册信息2}), ('/register', {注册信息3})]
    """
    
    def get_register_info():
        users_info = Data.read_user_yaml('register.yaml')
        # print(users_info)
        args_list = []
        for k, v in users_info.items():
            if k != 'url':
                args_list.append((users_info['url'], v))
    
        # print(args_list)
        return args_list
    
    def get_login_info():
        users_info = Data.read_user_yaml('login.yaml')
        # print(users_info)
        args_list = []
        for k, v in users_info.items():
            if k != 'url':
                args_list.append((users_info['url'], v))
    
        # print(args_list)
        return args_list
    
    class TestUsers(object):
        @pytest.mark.skipif(condition=True, reason='注册跳过哦')
        @pytest.mark.parametrize(['url', 'user_info'], get_register_info())
        def test_register(self, url, user_info):
            resp = Ask.post(url, data=user_info)
            print(resp.json())
            assert resp.status_code == 200, '注册失败'
    
        @pytest.mark.skipif(condition=False, reason='登陆跳过哦')
        @pytest.mark.parametrize(['url', 'user_info'], get_login_info())
        def test_login(self, url, user_info):
            resp = Ask.post(url, data=user_info)
            print(resp.json())
            assert resp.status_code == 200, '登陆失败'



  • 相关阅读:
    Cheatsheet: 2010 05.25 ~ 05.31
    Cheatsheet: 2010 07.01 ~ 07.08
    Cheatsheet: 2010 07.22 ~ 07.31
    Cheatsheet: 2010 06.01 ~ 06.07
    Cheatsheet: 2010 05.11 ~ 05.17
    Cheatsheet: 2010 06.08 ~ 06.15
    Cheatsheet: 2010 06.16 ~ 06.22
    Cheatsheet: 2010 06.23 ~ 06.30
    2020.7.20第十五天
    2020.7.19第十四天
  • 原文地址:https://www.cnblogs.com/zhangwei112/p/13635958.html
Copyright © 2020-2023  润新知