各位观众老爷,大家好!我是你们的老朋友,今天咱们不聊虚的,直接上干货,讲讲Python里搞BDD(Behavior-Driven Development,行为驱动开发)的那些事儿。
开场白:为什么要搞BDD?
话说码农的世界,变化总是比女朋友的心情还快。需求变来变去,代码改来改去,一不小心就写成了"屎山"。 怎么办?BDD 就像一盏明灯,照亮我们前进的方向。
BDD的核心思想是:用自然语言描述软件的行为,然后把这些描述变成自动化测试。 这样一来,开发人员、测试人员、产品经理甚至客户都能看懂,避免了沟通上的误解,也让测试更加贴近用户的真实需求。
主角登场:Behave 和 Lettuce
Python里搞BDD,最常用的两个工具就是 Behave 和 Lettuce。它们都遵循 Gherkin 语法,让你用自然语言来描述你的测试场景。
- Behave: 功能强大,社区活跃,文档完善,是目前Python BDD领域的事实标准。
- Lettuce: 比较轻量级,语法更简洁,适合小型项目或者快速原型开发。
咱们今天主要以 Behave 为例,讲讲怎么玩转 BDD。
Gherkin 语法:像写故事一样写测试
Gherkin 是一种简单的、人类可读的语言,用于描述软件的行为。它主要由以下几个关键字组成:
- Feature: 描述一个大的功能模块。
- Scenario: 描述一个具体的场景,也就是一个测试用例。
- Given: 描述场景的前提条件。
- When: 描述用户的行为。
- Then: 描述预期的结果。
- And: 连接多个 Given、When、Then。
- But: 和 And 类似,但表示一种例外情况。
- Scenario Outline: 描述多个类似的场景,可以用表格来参数化。
- Examples: 提供 Scenario Outline 中使用的参数。
举个例子,假设我们要测试一个简单的加法器:
# features/add.feature
Feature: 加法器
Scenario: 两个正数相加
Given 我输入数字 5
And 我输入数字 3
When 我点击“相加”按钮
Then 结果应该为 8
Scenario Outline: 多个数字相加
Given 我输入数字 <num1>
And 我输入数字 <num2>
When 我点击“相加”按钮
Then 结果应该为 <result>
Examples:
| num1 | num2 | result |
| 1 | 2 | 3 |
| 4 | 5 | 9 |
| 7 | 8 | 15 |
这个 add.feature
文件描述了加法器的两个场景:一个是两个正数相加,另一个是多个数字相加。Scenario Outline
允许我们用不同的参数来运行同一个场景,避免了重复编写代码。
Behave 的使用:把故事变成代码
有了 feature
文件,接下来就要用 Behave 来运行它。首先,你需要安装 Behave:
pip install behave
然后,在你的项目目录下创建一个 features
目录,把 add.feature
文件放进去。接下来,创建一个 features/steps
目录,用来存放测试步骤的实现代码。
# features/steps/add.py
from behave import *
@given('我输入数字 {num}')
def step_impl(context, num):
context.num = int(num)
@when('我点击“相加”按钮')
def step_impl(context):
context.result = context.num + context.num
@then('结果应该为 {result}')
def step_impl(context, result):
assert context.result == int(result)
这个 add.py
文件定义了三个测试步骤:
@given('我输入数字 {num}')
:这个步骤对应Given
关键字,它把输入的数字保存到context
对象中。@when('我点击“相加”按钮')
:这个步骤对应When
关键字,它执行加法操作,把结果保存到context
对象中。@then('结果应该为 {result}')
:这个步骤对应Then
关键字,它验证结果是否符合预期。
现在,你就可以运行 Behave 了:
behave
Behave 会自动找到 features
目录下的所有 feature
文件,然后按照 Gherkin 语法执行测试步骤。如果一切顺利,你会看到类似这样的输出:
1 feature passed, 0 failed, 0 skipped
3 scenarios passed, 0 failed, 0 skipped
9 steps passed, 0 failed, 0 skipped, 0 undefined
Took 0m0.000s
这意味着所有的测试都通过了!
高级技巧:Context 对象
在 Behave 中,context
对象是一个非常重要的东西。它可以用来在不同的测试步骤之间传递数据。例如,在上面的例子中,我们用 context.num
和 context.result
来保存输入的数字和计算结果。
你还可以在 context
对象中添加自定义的属性和方法。例如,你可以创建一个 context.browser
对象来模拟浏览器行为,或者创建一个 context.database
对象来访问数据库。
表格:参数化测试的利器
Scenario Outline
和 Examples
允许我们用表格来参数化测试。这在测试多个类似的场景时非常有用。
例如,假设我们要测试一个计算器,它可以进行加法、减法、乘法和除法运算:
Feature: 计算器
Scenario Outline: 计算器可以进行各种运算
Given 我输入数字 <num1>
And 我输入数字 <num2>
When 我点击“<operator>”按钮
Then 结果应该为 <result>
Examples:
| num1 | num2 | operator | result |
| 1 | 2 | + | 3 |
| 4 | 5 | - | -1 |
| 7 | 8 | * | 56 |
| 9 | 3 | / | 3 |
对应的测试步骤实现代码如下:
from behave import *
@given('我输入数字 {num}')
def step_impl(context, num):
context.num = int(num)
@when('我点击“{operator}”按钮')
def step_impl(context, operator):
if operator == '+':
context.result = context.num + context.num
elif operator == '-':
context.result = context.num - context.num
elif operator == '*':
context.result = context.num * context.num
elif operator == '/':
context.result = context.num / context.num
else:
raise ValueError('Invalid operator: {}'.format(operator))
@then('结果应该为 {result}')
def step_impl(context, result):
assert context.result == int(result)
通过使用表格,我们可以用更少的代码来测试更多的场景,提高了测试效率。
钩子函数:在测试前后做一些事情
Behave 提供了钩子函数,允许你在测试前后执行一些代码。例如,你可以在测试开始前启动一个 Web 服务器,或者在测试结束后关闭它。
Behave 提供了以下几种钩子函数:
before_all(context)
:在所有测试开始前执行。after_all(context)
:在所有测试结束后执行。before_feature(context, feature)
:在每个 feature 开始前执行。after_feature(context, feature)
:在每个 feature 结束后执行。before_scenario(context, scenario)
:在每个 scenario 开始前执行。after_scenario(context, scenario)
:在每个 scenario 结束后执行。before_step(context, step)
:在每个 step 开始前执行。after_step(context, step)
:在每个 step 结束后执行。
你可以在 features/environment.py
文件中定义这些钩子函数。例如:
# features/environment.py
def before_all(context):
print('测试开始!')
def after_all(context):
print('测试结束!')
def before_scenario(context, scenario):
print('Scenario: {} 开始!'.format(scenario.name))
def after_scenario(context, scenario):
print('Scenario: {} 结束!'.format(scenario.name))
这些钩子函数可以让你在测试过程中做一些初始化和清理工作,例如:
- 连接数据库
- 启动 Web 服务器
- 创建测试数据
- 清理临时文件
Behave 配置:让测试更灵活
Behave 允许你通过配置文件来定制测试行为。你可以创建一个 behave.ini
文件,放在你的项目目录下,用来配置 Behave 的各种选项。
例如,你可以用 behave.ini
文件来指定 feature 文件的路径、测试步骤的路径、报告的格式等等。
# behave.ini
[behave]
paths = features
steps_path = features/steps
format = pretty
这个配置文件指定了:
paths = features
:feature 文件放在features
目录下。steps_path = features/steps
:测试步骤的实现代码放在features/steps
目录下。format = pretty
:使用pretty
格式来输出测试报告。
Behave 提供了很多配置选项,你可以根据自己的需要来定制测试行为。
Lettuce:轻量级的选择
虽然我们主要讲的是 Behave,但 Lettuce 也是一个不错的选择,特别是对于小型项目或者快速原型开发。
Lettuce 的语法和 Behave 类似,但更简洁一些。例如,Lettuce 没有 Feature
关键字,而是直接用 Scenario
来描述一个测试用例。
# features/add.feature (Lettuce)
Scenario: 两个正数相加
Given 我输入数字 5
And 我输入数字 3
When 我点击“相加”按钮
Then 结果应该为 8
Lettuce 的测试步骤实现代码也更简洁一些:
# features/steps.py (Lettuce)
from lettuce import *
@step('我输入数字 (.*)')
def step_impl(step, num):
world.num = int(num)
@step('我点击“相加”按钮')
def step_impl(step):
world.result = world.num + world.num
@step('结果应该为 (.*)')
def step_impl(step, result):
assert world.result == int(result)
在 Lettuce 中,我们用 world
对象来代替 Behave 的 context
对象,用来在不同的测试步骤之间传递数据。
总的来说,Lettuce 比 Behave 更轻量级,更易于上手,但功能也相对简单一些。你可以根据自己的需要来选择合适的工具。
BDD 的最佳实践:让你的测试更有效
- 用自然语言描述测试场景:让开发人员、测试人员、产品经理甚至客户都能看懂。
- 从用户的角度思考:测试应该模拟用户的真实行为,而不是仅仅验证代码的内部逻辑。
- 保持测试的简洁性:每个测试用例应该只测试一个功能点,避免测试过于复杂。
- 编写可维护的测试代码:测试代码也需要精心设计,避免重复代码,提高可读性和可维护性。
- 持续集成:把 BDD 测试集成到持续集成流程中,及时发现和修复 Bug。
总结:BDD,让开发更美好
BDD 是一种非常有用的软件开发方法,它可以帮助我们更好地理解用户需求,提高代码质量,减少沟通成本。Behave 和 Lettuce 是 Python 中两个常用的 BDD 工具,它们都遵循 Gherkin 语法,让你用自然语言来描述你的测试场景。
希望今天的讲座能帮助你更好地理解和应用 BDD。记住,编程的道路漫长而艰辛,但只要我们坚持学习,不断进步,就能创造出更美好的软件世界!
一个表格总结 Behave 和 Lettuce 的区别:
特性 | Behave | Lettuce |
---|---|---|
社区活跃度 | 非常活跃,文档完善 | 相对较小,文档较少 |
功能 | 功能强大,配置灵活 | 相对简单,配置较少 |
语法 | 遵循 Gherkin 语法 | 遵循 Gherkin 语法,更简洁 |
Context对象 | 使用 context 对象传递数据 |
使用 world 对象传递数据 |
适用场景 | 大型项目,需要灵活配置 | 小型项目,快速原型开发 |
希望这个表格可以帮助你更好地选择合适的 BDD 工具! 咱们下期再见!