pytest-bdd (1)- 入门

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情

什么是bdd

敏捷开发

  • 一种软件研发流程模型
  • 互联网:给的钱多,技术要求高,不是瀑布模型、敏捷开发

三大角色

  • PO:产品负责人

  • PM/SM:管理者、项目管理

  • Scrum Team:敏捷团队

一些概念

  • Product Backlog:客户需求,由产品经理负责;产品功能列表一一罗列出来,内容多,不可一次性开发完成
  • Sprint Backlog:每次迭代,需要完成的需求
  • Story:用户故事,高质量描述需求,比如:作为一个[用户],我想要[功能],以便我可以[获得价值]
  • Feature:需求背景

注意

敏捷,意味着需求可能经常性变动,要拥抱变化

BDD

行为驱动开发

站在测试角度来理解:产品经理编写一个指定格式的文档,自动化测试程序可以根据文档的描述去自动化执行测试用例

pytest-bdd 介绍

官方 github

github.com/pytest-dev/…

官方文档

pytest-bdd.readthedocs.io/en/latest/

安装

pip install pytest-bdd
复制代码

快速入门小栗子

quick.feature

创建一个 quick.feature文件

Feature: Blog
    A site where you can publish your articles.

    Scenario: Publishing the article
        Given I'm an author user
        And I have an article

        When I go to the article page
        And I press the publish button

        Then I should not see the error message
        And the article should be published
复制代码

test_quick.py

创建一个 test_quick.py文件

import pytest
from pytest_bdd import scenario, given, when, then


# pytest fixture
# use for the following step
@pytest.fixture
def auth():
    return dict()


@pytest.fixture
def author():
    return dict(user={"name": "polo", "age": 24})


# given step
@given("I'm an author user")
def author_user(auth, author: dict):
    auth['user'] = author['user']
    print("=== auth is ", auth)


# second given step
@given("I have an article", target_fixture="article")
def article(author):
    print("=== fixture article: author is ", author)
    return author


# when
@when("I go to the article page")
def go_to_article(article):
    print("=== go to article ", article)


@when("I press the publish button")
def publish_article():
    print("=== opeate: press publish button")


# then
@then("I should not see the error message")
def no_error_message():
    print("=== expect: should not see the error message")
    assert 1 == 1


@then("the article should be published")
def article_is_published(article):
    print("=== expect: the article should be published")
    assert article == dict(user={"name": "polo", "age": 24})


# scenario
@scenario('quick.feature', 'Publishing the article')
def test_publish():
    pass
复制代码

pytest 运行

在上述两个文件所在目录下,命令行敲

pytest -sq test_quick.py
复制代码

运行结果

=== auth is  {'user': {'name': 'polo', 'age': 24}}
=== fixture article: author is  {'user': {'name': 'polo', 'age': 24}}
=== go to article  {'user': {'name': 'polo', 'age': 24}}
=== opeate: press publish button
=== expect: should not see the error message
=== expect: the article should be published
.
1 passed in 0.01s
复制代码

来拆解知识点了

@scenario

这是场景装饰器,会在最后执行,扒源码

def scenario(feature_name: str, scenario_name: str, encoding: str = "utf-8", features_base_dir=None):
    """Scenario decorator.
    
    :param str feature_name: Feature file name. Absolute or relative to the configured feature base path.
    :param str scenario_name: Scenario name.
    :param str encoding: Feature file encoding.
    """
复制代码

前两个位置参数必传,参数解析也很明白了

  • feature_name: feature 文件的名,绝对/相对于设置的 feature base path 路径
  • scenario_name: 在 feature 文件中的 Scenario 关键字后面跟的字符串,场景名称

假设不写这个装饰器会怎么样

# scenario
# @scenario('quick.feature', 'Publishing the article')
# def test_publish():
#    pass
复制代码

再次运行 pytest 命令

pytest -sq test_quick.py
复制代码

运行结果

> pytest -sq test_quick.py

no tests ran in 0.00s
复制代码

没有识别到对应的测试用例

feature 文件的拆解

重要的几个基础关键字

这里用中文来描述一个具体的🌰

Feature: 需求标题,官方网址 - 登录功能
    下面是对官方网址 - 登录功能进行设计多个测试场景
    以下的描述,可以由非测试人员编写:Feature、Scenario、Given、When、And、Then
    通过特定的关键字 + 自然语言,来描述一个用户的操作行为

    Scenario: 场景1 - 正常登录
        # Given 数据定义,可以只有一个;如果要有多个,需要用 And 关键字
        Given 正常登录的用户,用户名:admin 密码:123456
        And 这个登录用户的详细信息,性别:女,年龄:24

        # When 测试具体的步骤
        When 打开登录页面
        And 输入用户名
        And 输入密码
        And 点击登录按钮

        # Then 进行判断
        Then 登录成功,判断页面链接是否为跳登录跳转后页面的链接
        And 验证用户名是否一致

    Scenario: 场景2 - 登录失败
        ...
复制代码
  • Feature:需求背景,一般第一行写一个需求标题,后面几行可以写需求具体的描述;一个 feature 只能有一个 Feature 关键字
  • Scenario:测试场景,一个 feature 文件可以有多个场景 Scenario,区分名字即可
  • Given:前置操作,一般用来做预置条件/准备数据,如果有多个可以换行使用 And 关键字
  • And:表示当前关键字作用域未结束
  • When:具体的操作/测试步骤
  • Then:后置操作,一般用来验证结果(断言),数据清理等操作

整体的流程可以简单看成

Given - When - Then

准备 - 执行 - 验证

step

背景

不同 Scenario 都有 Given,不同的 Given 可以共同调用一个函数

second.feature

Feature: Resource owner
    Scenario: I'm the author
        Given I'm an author
        And I have an article


    Scenario: I'm the admin
        Given I'm the admin
        And there's an article
复制代码

test_second.py python 文件

import pytest
from pytest_bdd import given, scenario


@given("I'm an author", target_fixture="author")
def author():
    print("I'm an author")
    return dict(username="polo")


@given("I'm the admin", target_fixture="author")
def admin():
    print("I'm an admin")
    return dict(username="admin")


@given("I have an article")
@given("there's an article")
def article(author):
    print("two given")
    return author


# scenario
@scenario('second.feature', "I'm the author")
def test_publish():
    pass


@scenario('second.feature', "I'm the admin")
def test_publish2():
    pass
复制代码

不同 Scenario 的 Given 可以装饰在同一个 Python 函数上

命令行运行

pytest -sq test_second.py
复制代码

运行结果

I'm an author
two given
.I'm an admin
two given
.
2 passed in 0.01s
复制代码

注意点

@given("I have an article")
@given("there's an article")
def article(author):
    print("two given")
    return author
复制代码

假设调用了一个 fixture(这里是 author)而且这个 fixture 是其他 step,那么必须拥有同名的 fixture 才能调用成功,否则会报错

@given("I'm an author", target_fixture="author")
def author():
    print("I'm an author")
    return dict(username="polo")


@given("I'm the admin", target_fixture="author")
def admin():
    print("I'm an admin")
    return dict(username="admin")
复制代码

可以看到,两个 given 的 target_fixture 是同名的

@given("I'm an author", target_fixture="author")
def author():
    print("I'm an author")
    return dict(username="polo")

# 假如这个 target_fixture 不是 author,那么运行就会报错:fixture 'author' not found
@given("I'm the admin", target_fixture="admin")
def admin():
    print("I'm an admin")
    return dict(username="admin")
复制代码
分类:
后端