pytest数据参数化和数据驱动yaml的简单使用

Pytest参数化

  • @pytest.mark.parametrize(argnames, argvalues)
  • argnames: 要参数化的变量, string(逗号分隔), list, tuple
  • argvalues: 参数化的值,list, list[tuple]

新建一个 简单的 test_demo.py 代码内容为:

class TestClass:
    @pytest.mark.parametrize('a,b', [(1, 2), (2, 3), (4, 5)])
    def test_a(self, a, b):
        # print(f'login name is {}')
        print(a + b)
        print('1')

    def test_b(self):
        print('2')

    def test_c(self, login):
        print(f'login name is {login}')
        print('3')
然后在控制台中 输入 上一节 学到的 -k(指定函数) -v(打印详细内容) -s (输出print内容)
pytest -v -k test_a -s

可以看到想要的结果

测试结构化的简单demo

读取yaml文件, 先建立一个数组的demo
yaml的语法 可以参考 https://www.runoob.com/w3cnote/yaml-intro.html

-
  - 10
  - 20
-
  - 30
  - 40

然后建立一个Pytest去读取

import pytest
import yaml

class TestClass:
    @pytest.mark.parametrize('a,b', yaml.safe_load(open('env.yml')))
    def test_a(self, a, b):
        print(a + b)
        print('1')

常常我们需要结构化我们的测试环境,一般配置文件放在yaml中,简单的读取

yml 文件内容:
-
  languages:
    - Ruby
    - Perl
    - Python 
  websites:
    YAML: yaml.org 
    Ruby: ruby-lang.org 
    Python: python.org 
    Perl: use.perl.org

  dev: # 生产环境
    ip: 127.0.0.1

---- # 注意点 当不知道自己写的yml格式对象是否正确的时候可以通过读取出来来验证
class TestClass:
   def test_b(self):
        print(yaml.safe_load(open('env.yml')))
# 然后执行命令行: pytest -v -k test_b -s 就可以看到写的yml

# 下面是读取配置文件的写法,前提是知道自己的yml是什么样子
import pytest
import yaml
class TestClass:
    @pytest.mark.parametrize('env', yaml.safe_load(open('env.yml')))
    def test_a(self, env):
        if "dev" in env:
            # print(env) 可以先打印出来看看 是什么结构
            print(f'生产环境, ip地址为{env.get("dev").get("ip")}')
        elif "test" in env:
            print('测试环境')
然后执行命令: pytest -v -k test_a -s 就可以看到执行的结果

关于和unittest的一些差不多的用法

有两个前置方法,两个后置方法

  • setup()
  • setupClass()
  • teardown()
  • teardownClass()

函数级别用法demo, 新建一个test_demo2.py文件:

import pytest

def setup_module():
    print("py模块开始前只执行一次,打开)


def teardown_module():
    print("py模块结束后只执行一次,关闭")


def setup_function():
    print("函数开始前都执行setup_function)


def teardown_function():
    print("函数结束后都执行teardown_function")


def test_one():
    print("one")


def test_two():
    print("two")


if __name__ == '__main__':
    pytest.main(["-q", "-s", "-ra", "test_demo2.py"])  # -q 简单显示结果

执行即可得到结果

类级别用法demo, 新建一个test_demo3.py文件:

import pytest


class TestCase():
    def setup_class(self):
        print("====整个测试类开始前只执行一次setup_class====")

    def teardown_class(self):
        print("====整个测试类结束后只执行一次teardown_class====")

    def setup_method(self):
        print("==类里面每个用例执行前都会执行setup_method==")

    def teardown_method(self):
        print("==类里面每个用例结束后都会执行teardown_method==")

    def setup(self):
        print("=类里面每个用例执行前都会执行setup=")

    def teardown(self):
        print("=类里面每个用例结束后都会执行teardown=")

    def test_three(self):
        print("one")

    def test_four(self):
        print("two")


if __name__ == '__main__':
    pytest.main(["-q", "-s", "-ra", "test_demo3.py"])  # -q 简单显示结果

当遇到一些需要针对某个测试用例使用的,比如登陆什么的时候,上面这种针对整个脚本全局生效的,明显就不合适使用了,所以要用上一节提到的 @pytest.fixture(),就可以解决这些场景,但是当有多个测试用例文件(test_*.py)的所有用例都需要用登录功能来作为前置操作,那就不能把登录功能写到某个用例文件中去,所以需要一个conftest.py来管理一些全局的fixture

conftest.py配置fixture注意事项

  • conftest.py 会被pytest 默认读取,不用导入
  • 文件名是固定的,一个包可以有一个,一个项目可以有多个

项目结构如下:

conftest.py代码

最顶层的conftest.py,一般写全局的fixture

import pytest

# scope:可以理解成fixture的作用域,默认:function,还有class、module、package、session四个【常用】
# function 每一个函数或方法都会调用
# class 每一个类调用一次,一个类可以有多个方法
# module,每一个.py文件调用一次,该文件内又有多个function和class
# session 是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

@pytest.fixture(scope="session")
def login():
    print("====登录功能,返回账号,token===")
    name = "lakes"
    token = "something you ned guess!"
    yield name, token
    print("====退出登录!!!====") # 退出登录,最后才会执行

# autouse:默认:False,需要用例手动调用该fixture;如果是True,所有作用域内的测试用例都会自动调用该fixture
@pytest.fixture(autouse=True)
def get_info(login):
    name, token = login
    print(f"== 每个用例都调用的外层fixture:打印用户token: {token} ==")`

@pytest.fixture()
def get_test():
    print('测试autouse=False')  # 不会打印

test_demo1.py

def test_get_info(login):
    name, token = login
    print("***基础用例:获取用户个人信息***")
    print(f"用户名:{name}, token:{token}")

01_run.py 运行主文件

import pytest

if __name__ == '__main__':
    pytest.main(["-s", "../test_01/"]) # 执行入口

主要的测试可以正常运行的话,就可以加入一些其他平台的测试

选了微博小红书和今日头条来做实验,现在的目录结构是这这样子

外部是上面的内容。加了上个新的文件夹,分别是 test_redbook 、test_toutiao、 test_weibo

test_redbook/test_red1.py 文件内容 什么都不加,只是调用最外层的conftest.py的 fixture,login

def test_no_fixture(login):
    print("==没有__init__测试用例,我进入小红书了==", login) # 输出打印的只是这句话

test_toutiao/conftest.py 项目里面除了最外层的conftest.py 还新增了一个conftest, 这个这个目录下的conftest.py, 最外层的可以用,目录下的也可以用

import pytest

# 针对头条独有的,function代表每一个函数或方法都会调用
@pytest.fixture(scope="function")
def open_toutiao(login):
    name, token = login
    print(f"&&& 用户 {name} 返回头条首页 &&&")

test_toutiao/test_t1.py 来测试function是否生效,在测试用例执行前会执行一遍,这里执行了两次

class TestToutiao:
    def test_case1_01(self, open_toutiao):
        print("查看头条今日关注")

    def test_case1_02(self, open_toutiao):
        print("查看头条今日的用户")

test_weibo/conftest.py 这个跟上面的一样,但是scope进行了更改

import pytest


@pytest.fixture(scope="module")
def open_weibo(login):
    name, token = login
    print(f"###用户 {name} 打开微博 ###")


@pytest.fixture()
def close_weibo(login):
    name, token = login
    print(f"###用户 {name} 离开了微博 ###")

test_weibo/test_case1.py 来测试function是否生效,在测试用例中只执行了一遍

def test_case2_01(open_weibo):
    print("微博超话内容")


def test_case2_02(open_weibo):
    print("微博热点内容")


def test_case2_04(close_weibo):
    print("微博关闭44444444")


def test_case2_05(close_weibo):
    print("微博关闭555555")

然后直接执行 python 01_run.py
完。
参考: 小菠萝测试笔记 https://www.cnblogs.com/poloyy/p/12641991.html
下一次随笔写的是测试报告的美化与定制 Allure测试框架的使用

posted @ 2021-05-27 01:04  陈科科  阅读(1807)  评论(0编辑  收藏  举报