什么是 BDD 测试?框架示例
什么是 BDD(行为驱动开发)测试?
BDD(行为驱动开发)测试 是敏捷软件开发的一种技术,是 TDD(即测试驱动开发)的扩展。在 BDD 中,测试用例以自然语言编写,即使是非程序员也可以读懂。
BDD 测试如何工作?
假设您被指派在网上银行应用程序中创建资金转账模块。
有多种方法可以测试它
- 如果源账户有足够的余额,则应进行资金转账
- 如果目的地账户详细信息正确,则应进行资金转账
- 如果用户输入的交易密码/rsa代码/交易安全认证正确,则应进行资金转账
- 即使是银行假日也应进行资金转账
- 资金转账应在账户持有人设定的未来日期进行
- 测试场景 随着我们考虑其他特征(例如,在 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
安装方式:
- 安装软件 Python 3从 https://www.python.org/
- 在命令提示符下执行以下命令来安装表现
- pip 安装表现
- IDE:我使用过 PyCharm 社区版 https://www.jetbrains.com/pycharm/download
项目设置:
- 创建一个新项目
- 创建以下目录结构:
功能文件:
那么让我们构建我们的功能文件 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 格式的测试报告
测试报告显示个别场景结果
总结
- BDD 是行为驱动开发。它是敏捷软件开发的技术之一。
- 如今,REST 已经成为构建 API 的一种相当流行的风格,自动化 REST API 测试用例与 UI 测试用例变得同等重要。
- BDD 具有自然语言格式,描述特征或特征的一部分,并附有预期结果的代表性示例。
- Behave 框架通过与特征文件谓词匹配的装饰器来识别 Step 函数
- BDD 测试框架示例:1) Cucumber 2)SpecFlow 3)Quantum 4)JBehave 5)Codeception