• pytest-bdd快速示例和问题解决


    BDD 与 pytest-bdd

    BDD 即 Behavior-driven development,行为驱动开发。BDD行为驱动是一种敏捷开发模式, 重点在于消除开发/测试对需求了解的歧义及用户场景的验证。
    pytest-bdd 是一个BDD测试框架,类似于behave, cucumber。它可以统一单元测试和功能测试。

    环境准备

    首先需要确定是否安装了pytest-bdd框架,通过 pip show pytest-bdd 命令可以查看是否安装了pytest-bdd框架:

    在这里插入图片描述

    出现如上页面说明没有安装,安装pytest-bdd框架:使用pip安装pytest-bdd模块。

    pip install pytest-bdd
    
    • 1

    备注: 也可以使用 pip list 命令查看所有已经安装的模块。

    ##示例场景与步骤

    这里测试一个加法运算器, 在实际场景中验证加法使用Unit 测试即可,BDD 一般用作功能测试,这里为了演示方便使用该场景作为示例。

    具体步骤如下:

    1. 创建用户场景文件(.feature后缀的文件)
    2. 编写步骤函数和测试场景。(这两者可以分开为不同文件,也可以合并在一起写)
    3. 开始测试

    项目目录

    pytest-bdd 对于目录的要求非常灵活,可以根据自己的项目结构进行配置。通常情况下,pytest-bdd 会将 feature 文件和 step 实现文件分别放置在不同的目录中,这两个目录可以分别为features/step_defs/,也可以根据自己的需要进行修改。同时,pytest-bdd 还可以支持多个 feature 目录和 step 实现目录,只需要在配置文件中进行相应的配置即可。

    除了 feature 文件和 step 实现文件的目录外,pytest-bdd 还可以支持其他的目录,例如 fixture 目录、data 目录等。这些目录同样可以根据自己的需要进行配置。总之,pytest-bdd 的目录要求非常灵活,可以根据自己的项目进行配置。

    简单起见, 这里演示的目录结构如下所示:

    ├────features/   # 用户场景
    │    ├────calculator.feature
    
    ├────step_defs/  # 步骤函数和测试场景
    │    ├────test_calculator.py
    ├────util/
    │    └────calculator.py # 需要测试的计算器类
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    文件内容

    1. 用于测试的计算器类文件calculator.py的内容如下:
    # calculator.py
    class Calculator:
        def add(self, a, b):
            return a + b
    
        def subtract(self, a, b):
            return a - b
    
        def multiply(self, a, b):
            return a * b
    
        def divide(self, a, b):
            if b == 0:
                raise ValueError("Cannot divide by zero")
            return a / b
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 用户场景文件calculator.feature 内容如下:
    Feature: Addition
      Scenario: Add two numbers
        Given I have a calculator
        When I enter "1" and "2"
        Then the result should be "3"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 步骤函数和测试场景文件test_calculator.py 内容如下:
    import sys
    import os
    import pytest
    #sys.path.append('D:/devworkspace/python-ency/chp3/tests/bdd/util')
    sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'util'))
    from calculator import Calculator
    from pytest_bdd import scenario, given, when, then, parsers
    
    
    
    @scenario('../features/calculator.feature','Add two numbers')
    def test_add():
        print(sys.path.append(os.path.dirname(os.path.dirname(__file__))+'util'))
        pass
    
    @pytest.fixture
    @given("I have a calculator")
    def calculator():
        return Calculator()
    
    @when(parsers.parse('I enter "{a}" and "{b}"'))
    def enter_numbers(calculator, a, b):
        calculator.a = int(a)
        calculator.b = int(b)
    
    @then(parsers.parse('the result should be "{result}"'))
    def verify_result(calculator, result):
        assert calculator.add(calculator.a, calculator.b) == int(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试

    命令行切换到对应的目录,执行 pytest即可。执行的效果如下图:
    在这里插入图片描述

    问题

    自定义类的导入方式

    from calculator import Calculator
    E   ModuleNotFoundError: No module named 'calculator'
    
    • 1
    • 2
    1. 添加绝对路径导入
    import sys
    sys.path.append("E:/mybdd/util")
    
    • 1
    • 2
    1. 添加上层目录
    import sys
    import os
    sys.path.append(os.path.dirname(os.path.dirname(__file__)))
    
    • 1
    • 2
    • 3

    修改sys.path之后的导入行为会对你的整个 Python 环境产生影响,使用时需要谨慎。

    1. 相对导入
      .来表示当前目录,两个(或更多的)点..来表示上一层(或更多层)目录
      相对导入只有在作为模块的一部分时才能工作,也就是说,你不能直接运行一个使用了相对导入的 Python 文件,你需要通过主模块或者 -m 标志运行。
      Python 的相对导入基于当前的模块名称。所以,你不能在一个脚本直接运行时使用相对导入,因为脚本的__name__属性为__main__,Python 就不知道如何找到父模块或者兄弟模块。
      将你的脚本作为模块执行
      你可以使用-m选项来告诉 Python 运行包含相对导入的脚本作为一个模块。首先,确保你在当前执行目录的上级目录中,然后使用类似以下的命令:
    
    python -m mypackage.mysubpackage.mymodule
    
    
    • 1
    • 2
    • 3

    对于相对导入,你必须以包的方式运行你的项目(也就是说目录下需要有__init__.py文件,让Python把这个目录看作包),例如你可能需要在项目的顶层目录下运行python -m main,而非python main.py

    1. 使用环境变量 PYTHONPATH

    也可以通过在环境变量PYTHONPATH中添加你的模块所在的目录,Python 在运行时会添加这些目录到sys.path中,这样就可以搜索到你的模块了。
    export PYTHONPATH=“${PYTHONPATH}:/my/new/path”

    fixture ‘calculator’ not found

    @pytest.mark.usefixtures(*func_args)
              def scenario_wrapper(request: FixtureRequest, _pytest_bdd_example: dict[str, str]) -> Any:
    E       fixture 'calculator' not found
    
    • 1
    • 2
    • 3

    注意下面代码

    @pytest.fixture
    @given("I have a calculator")
    def calculator():
        return Calculator()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    @pytest.fixture 这一句必须加上, 很多在线的例子中都没有

    ERROR test_calculator.py - TypeError: ‘NoneType’ object is not callable

    @scenario('../features/calculator.feature','Add two numbers')
    #@scenarios('../features/calculator.feature')
    def test_add():
        pass
    
    • 1
    • 2
    • 3
    • 4

    注意:使用scenarios会出现错误

    本篇完整示例

    参考



  • 相关阅读:
    举个栗子~Tableau 技巧(232):用工作表创建多行列图例
    基于Hata模型的BPSK调制信号小区覆盖模拟matlab完整程序分享
    基于NodeJS + Swagger UI搭建Web API界面
    看完阿里最新产500页微服务架构笔记,感觉我格局太小
    【IOS-初学】利用分段选择器和滑动开关(条)等等实现简单的图片浏览器-透明度和图片切换功能
    常见程序首页添加备案编号链接工信部
    ArrayList集合中元素的排序
    单页应用(SPA)和多页应用(MPA)的区别和优缺点?
    (原创)多线程并发:AQS源码分析(2)——共享锁的实现原理
    【云原生 • Kubernetes】一文掌握 k8s 包管理工具 Helm
  • 原文地址:https://blog.csdn.net/oscar999/article/details/134388175