持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
什么是bdd
敏捷开发
- 一种软件研发流程模型
- 互联网:给的钱多,技术要求高,不是瀑布模型、敏捷开发
三大角色
-
PO:产品负责人
-
PM/SM:管理者、项目管理
-
Scrum Team:敏捷团队
一些概念
- Product Backlog:客户需求,由产品经理负责;产品功能列表一一罗列出来,内容多,不可一次性开发完成
- Sprint Backlog:每次迭代,需要完成的需求
- Story:用户故事,高质量描述需求,比如:作为一个[用户],我想要[功能],以便我可以[获得价值]
- Feature:需求背景
注意
敏捷,意味着需求可能经常性变动,要拥抱变化
BDD
行为驱动开发
站在测试角度来理解:产品经理编写一个指定格式的文档,自动化测试程序可以根据文档的描述去自动化执行测试用例
pytest-bdd 介绍
官方 github
官方文档
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")
复制代码