什么是 BDD 测试?框架示例

什么是 BDD(行为驱动开发)测试?

BDD(行为驱动开发)测试 是敏捷软件开发的一种技术,是 TDD(即测试驱动开发)的扩展。在 BDD 中,测试用例以自然语言编写,即使是非程序员也可以读懂。

BDD 测试如何工作?

假设您被指派在网上银行应用程序中创建资金转账模块。

有多种方法可以测试它

  1. 如果源账户有足够的余额,则应进行资金转账
  2. 如果目的地账户详细信息正确,则应进行资金转账
  3. 如果用户输入的交易密码/rsa代码/交易安全认证正确,则应进行资金转账
  4. 即使是银行假日也应进行资金转账
  5. 资金转账应在账户持有人设定的未来日期进行

- 测试场景 随着我们考虑其他特征(例如,在 Y 天/月的时间间隔内转账金额为 X,当总金额达到 Z 时停止计划转账等),转账过程将变得更加精细和复杂。

开发人员的普遍倾向是先开发功能,然后再编写测试代码。如上例所示, 测试用例 本案的开发很复杂,开发商将推迟 测试 直到发布,此时他会进行快速但无效的测试。

为了解决这个问题(行为驱动开发),BDD 应运而生。它让开发人员的整个测试过程变得简单

在 BDD 中,无论你写什么都必须进入 给定-何时-然后 步骤。让我们在 BDD 中考虑上述相同示例

Given that a fund transfer module in net banking application has been developed
And I am accessing it with proper authentication
WhenI shall transfer with enough balance in my source account
Or I shall transfer on a Bank Holiday
Or I shall transfer on a future date
And destination a/c details are correct
And transaction password/rsa code / security authentication for the transaction is correct
And press or click send button
Then amount must be transferred
And the event will be logged in log file

编写、阅读和理解起来是不是很容易?它涵盖了资金转移模块的所有可能测试用例,并且可以轻松修改以容纳更多内容。此外,它更像是为资金转移模块编写文档。

什么是 REST API 测试?

如今,REST 已成为一种非常流行的 API 构建风格,因此,自动化 REST API 测试用例与 UI 测试用例同样重要。因此,基本上,这些 REST API测试 涉及分别使用方法 POST、GET、PUT 和 DELETE 测试 CRUD(创建-读取-更新-删除)操作。

Behave 是什么?

Behave 是最受欢迎的 Python BDD 测试框架。

让我们看看 Behave 如何发挥作用:

功能文件由您的业务分析师/赞助商/任何人编写,其中包含您的行为场景。它具有自然语言格式,描述功能或功能的一部分,并附有预期结果的代表性示例

这些场景步骤与步骤实现相对应,具体写在 Python.

并且可选地,有一些环境控制(在步骤、场景、特征或整个射击比赛之前和之后运行的代码)。

让我们开始使用 Behave 设置我们的自动化测试框架:

设置 BDD 测试框架 Behave on Windows

安装方式:

项目设置:

  • 创建一个新项目
  • 创建以下目录结构:

项目设置

功能文件:

那么让我们构建我们的功能文件 Sample_REST_API_Testing.feature 具有对“帖子”服务执行 CRUD 操作的功能。

在我们的例子中,我使用了 http://jsonplaceholder.typicode.com/ 发布示例 REST 服务。

POST 场景示例

Scenario: POST post example ->Here we are considering creating new post item using 'posts' service
Given: I set post posts API endpoint ->This is prerequisite for the test which is setting URL of posts service
When: I set HEADER param request content type as "application/json."
And set request body
And send POST HTTP request ->This is actual test step of sending a post request
Then: Then I receive valid HTPP response code 201 
And Response body "POST" is non-empty-> This is verification of response body	

类似地,您可以按如下方式编写其余场景:

项目设置

Sample_REST_API_Testing.feature

Feature: Test CRUD methods in Sample REST API testing framework

Background:
	Given I set sample REST API url

Scenario: POST post example
  Given I Set POST posts api endpoint
 When I Set HEADER param request content type as "application/json." 
    And Set request Body
 And Send a POST HTTP request 
 Then I receive valid HTTP response code 201
    And Response BODY "POST" is non-empty. 


Scenario: GET posts example
  Given I Set GET posts api endpoint "1"
  When I Set HEADER param request content type as "application/json." 
	And Send GET HTTP request
  Then I receive valid HTTP response code 200 for "GET." 
	And Response BODY "GET" is non-empty


Scenario: UPDATE posts example
  Given I Set PUT posts api endpoint for "1"
  When I Set Update request Body
	And Send PUT HTTP request
  Then I receive valid HTTP response code 200 for "PUT." 
	And Response BODY "PUT" is non-empty


Scenario: DELETE posts example
  Given I Set DELETE posts api endpoint for "1"
  When I Send DELETE HTTP request
  Then I receive valid HTTP response code 200 for "DELETE." 

步骤实施

现在,对于上述场景中使用的功能步骤,您可以在 Python “steps”目录中的文件。

Behave 框架通过与特征文件谓词匹配的装饰器来识别 Step 函数。例如,特征文件场景中的 Given 谓词会搜索具有装饰器“given”的步骤函数。对于 When 和 Then 也会发生类似的匹配。但在“But”、“And”的情况下,Step 函数采用与其前一个步骤相同的装饰器。例如,如果 Given 的装饰器为“And”,则匹配的步骤函数装饰器为 @given。

例如,对于POST步骤可以如下实现:

@when (u'I Set HEADER param request content type as "{header_conent_type}"')
Mapping of When, here notice “application/json” is been passed from feature file for "{header_conent_type}” . This is called as parameterization


def step_impl (context, header_conent_type):
This is step implementation method signature

request_headers['Content-Type'] = header_conent_type
Step implementation code, here you will be setting content type for request header

类似地,步骤python文件中其他步骤的执行将如下所示:

步骤实施

示例步骤实现.py

from behave import given, when, then, step
import requests

api_endpoints = {}
request_headers = {}
response_codes ={}
response_texts={}
request_bodies = {}
api_url=None

@given(u'I set sample REST API url')
def step_impl(context):
    global api_url
    api_url = 'http://jsonplaceholder.typicode.com'

# START POST Scenario
@given(u'I Set POST posts api endpoint')
def step_impl(context):
    api_endpoints['POST_URL'] = api_url+'/posts'
    print('url :'+api_endpoints['POST_URL'])

@when(u'I Set HEADER param request content type as "{header_conent_type}"')
def step_impl(context, header_conent_type):
    request_headers['Content-Type'] = header_conent_type

#You may also include "And" or "But" as a step - these are renamed by behave to take the name of their preceding step, so:
@when(u'Set request Body')
def step_impl(context):
    request_bodies['POST']={"title": "foo","body": "bar","userId": "1"}

#You may also include "And" or "But" as a step - these are renamed by behave to take the name of their preceding step, so:
@when(u'Send POST HTTP request')
def step_impl(context):
    # sending get request and saving response as response object
    response = requests.post(url=api_endpoints['POST_URL'], json=request_bodies['POST'], headers=request_headers)
    #response = requests.post(url=api_endpoints['POST_URL'], headers=request_headers) #https://jsonplaceholder.typicode.com/posts
    # extracting response text
    response_texts['POST']=response.text
    print("post response :"+response.text)
    # extracting response status_code
    statuscode = response.status_code
    response_codes['POST'] = statuscode

@then(u'I receive valid HTTP response code 201')
def step_impl(context):
    print('Post rep code ;'+str(response_codes['POST']))
    assert response_codes['POST'] is 201
# END POST Scenario

# START GET Scenario
@given(u'I Set GET posts api endpoint "{id}"')
def step_impl(context,id):
    api_endpoints['GET_URL'] = api_url+'/posts/'+id
    print('url :'+api_endpoints['GET_URL'])

#You may also include "And" or "But" as a step - these are renamed by behave to take the name of their preceding step, so:
@when(u'Send GET HTTP request')
def step_impl(context):
    # sending get request and saving response as response object
    response = requests.get(url=api_endpoints['GET_URL'], headers=request_headers) #https://jsonplaceholder.typicode.com/posts
    # extracting response text
    response_texts['GET']=response.text
    # extracting response status_code
    statuscode = response.status_code
    response_codes['GET'] = statuscode

@then(u'I receive valid HTTP response code 200 for "{request_name}"')
def step_impl(context,request_name):
    print('Get rep code for '+request_name+':'+ str(response_codes[request_name]))
    assert response_codes[request_name] is 200

@then(u'Response BODY "{request_name}" is non-empty')
def step_impl(context,request_name):
    print('request_name: '+request_name)
    print(response_texts)
    assert response_texts[request_name] is not None
# END GET Scenario

#START PUT/UPDATE
@given(u'I Set PUT posts api endpoint for "{id}"')
def step_impl(context,id):
    api_endpoints['PUT_URL'] = api_url + '/posts/'+id
    print('url :' + api_endpoints['PUT_URL'])

@when(u'I Set Update request Body')
def step_impl(context):
    request_bodies['PUT']={"title": "foo","body": "bar","userId": "1","id": "1"}

@when(u'Send PUT HTTP request')
def step_impl(context):
    # sending get request and saving response as response object  # response = requests.post(url=api_endpoints['POST_URL'], headers=request_headers) #https://jsonplaceholder.typicode.com/posts
    response = requests.put(url=api_endpoints['PUT_URL'], json=request_bodies['PUT'], headers=request_headers)
    # extracting response text
    response_texts['PUT'] = response.text
    print("update response :" + response.text)
    # extracting response status_code
    statuscode = response.status_code
    response_codes['PUT'] = statuscode
#END PUT/UPDATE

#START DELETE
@given(u'I Set DELETE posts api endpoint for "{id}"')
def step_impl(context,id):
    api_endpoints['DELETE_URL'] = api_url + '/posts/'+id
    print('url :' + api_endpoints['DELETE_URL'])

@when(u'I Send DELETE HTTP request')
def step_impl(context):
    # sending get request and saving response as response object
    response = requests.delete(url=api_endpoints['DELETE_URL'])
    # response = requests.post(url=api_endpoints['POST_URL'], headers=request_headers) #https://jsonplaceholder.typicode.com/posts
    # extracting response text
    response_texts['DELETE'] = response.text
    print("DELETE response :" + response.text)
    # extracting response status_code
    statuscode = response.status_code
    response_codes['DELETE'] = statuscode
#END DELETE

运行测试

现在,我们已经完成了测试脚本的开发部分,所以让我们运行测试:

在命令提示符下执行以下命令来运行我们的功能文件

C:\程序\Python\Python37>行为举止 -f 漂亮 C:\ \功能\功能文件文件夹\Sample_REST_API_Testing.feature

这将显示测试执行结果如下:

运行测试

控制台上显示报告

让我们在这里再看看一件很酷的事情。

由于用户总是喜欢以更易读、更易于呈现的格式查看测试结果,因此让我们借助 Allure 以 HTML 格式获取报告。

业务报告

首先,你需要安装 Allure Behave 格式化程序 [https://docs.qameta.io/allure-report/]:

现在执行以下命令:

对于报告

>行为-f json-o Sample_REST_API_Testing.feature

> 诱惑服务

这将以如下可呈现且信息丰富的格式生成您的测试结果报告:

业务报告

HTML 格式的测试报告

HTML 格式的测试报告

测试报告显示个别场景结果

总结

  • BDD 是行为驱动开发。它是敏捷软件开发的技术之一。
  • 如今,REST 已经成为构建 API 的一种相当流行的风格,自动化 REST API 测试用例与 UI 测试用例变得同等重要。
  • BDD 具有自然语言格式,描述特征或特征的一部分,并附有预期结果的代表性示例。
  • Behave 框架通过与特征文件谓词匹配的装饰器来识别 Step 函数
  • BDD 测试框架示例:1) Cucumber 2)SpecFlow 3)Quantum 4)JBehave 5)Codeception