O que é o teste BDD? Framework de Desenvolvimento Orientado a Comportamento
⚡ Resumo Inteligente
O teste BDD descreve o comportamento da aplicação em uma linguagem simples de Dado-Quando-Então, permitindo que analistas, desenvolvedores e testadores compartilhem uma única especificação. Este guia aplica essa abordagem ao teste de API REST com o Behave, o Python estrutura, abrangendo configuração, arquivos de recursos, implementações de etapas, execução e geração de relatórios.
O que são testes BDD (Desenvolvimento Orientado a Comportamento)?
Teste BDD (desenvolvimento orientado a comportamento) BDD é uma técnica de desenvolvimento ágil de software e uma extensão do TDD, ou seja, Desenvolvimento Orientado a Testes. No BDD, os casos de teste são escritos em linguagem natural, de forma que até mesmo pessoas sem conhecimento de programação consigam lê-los.
Como funciona o teste BDD?
Imagine que você foi designado para criar um módulo de Transferência de Fundos em um aplicativo de Internet Banking.
Existem várias maneiras de testar isso:
- A transferência de fundos deve ser efetuada se houver saldo suficiente na conta de origem.
- A transferência de fundos deve ocorrer se os detalhes do ar-condicionado de destino estiverem corretos
- A transferência de fundos deverá ser efetuada se a senha da transação/código RSA/autenticação de segurança inserida pelo usuário estiver correta.
- A transferência de fundos deve ser efetuada mesmo em feriados bancários.
- A transferência de fundos deverá ocorrer em data futura definida pelo titular da conta
O Cenário de Teste A complexidade e a elaboração tornam-se mais elaboradas à medida que consideramos funcionalidades adicionais, como a transferência do montante X num intervalo de Y dias ou meses, ou a interrupção.ping uma transferência programada quando o total atingir Z, e assim por diante.
A tendência geral dos desenvolvedores é desenvolver funcionalidades e escrever o código de teste posteriormente. Como fica evidente no caso acima, Caso de teste O desenvolvimento aqui é complexo, então o desenvolvedor irá adiar. Testes até o lançamento, momento em que são realizados testes rápidos, porém ineficazes.
Para superar esse problema, foi concebido o Desenvolvimento Orientado a Comportamento (BDD). Ele facilita todo o processo de teste para o desenvolvedor.
No BDD, tudo o que você escreve deve entrar Dado-Quando-Então etapas. Vamos considerar o mesmo exemplo acima em BDD:
Given that a fund transfer module in net banking application has been developed And I am accessing it with proper authentication When I 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
Este formulário é fácil de escrever, ler e entender. Ele abrange todos os casos de teste possíveis para o módulo de transferência de fundos e pode ser modificado rapidamente para acomodar mais testes. Além disso, sua estrutura funciona como uma documentação viva do módulo. Como o BDD surgiu do TDD, é importante esclarecer a diferença entre os dois antes de partir para as ferramentas.
BDD vs TDD: Principais Diferenças
Ambas as práticas são orientadas a testes e ambas encurtam os ciclos de feedback. Elas diferem em quem escreve os testes, qual linguagem esses testes utilizam e qual camada da aplicação eles descrevem.
| Aspecto | TDD (Desenvolvimento Orientado a Testes) | BDD (Desenvolvimento Orientado a Comportamento) |
|---|---|---|
| Foco | Como funciona internamente uma unidade de código | Como o sistema se comporta da perspectiva do usuário |
| Língua | asserções de linguagem de programação | Linguagem natural: cenários Dado-Quando-Então |
| Autor principal | Desenvolvedor | Analista de negócios, dono do produto, testador e desenvolvedor juntos |
| Readable por não programadores | Não | Sim |
| Escopo típico | Nível da unidade | Funcionalidade, API e nível de aceitação |
| Ferramentas comuns | JUnit, pytest, NUnit | Comportar-se, CucumberSpecFlow, JBehave |
Os dois são complementares, e não concorrentes. As equipes geralmente mantêm TDD Para o design em nível de unidade, adicione cenários BDD para descrever o comportamento que as partes interessadas realmente aprovam. Como os endpoints REST se encontram exatamente nessa camada de aceitação, eles são uma opção natural para o BDD.
O que é teste de API REST?
Com a popularização do REST para a criação de APIs, a automação de casos de teste de API REST, juntamente com os casos de teste de interface do usuário, tornou-se igualmente importante. Teste de API Envolve testar ações CRUD (Criar-Ler-Atualizar-Excluir) com os métodos POST, GET, PUT e DELETE, respectivamente.
O que é Comportar-se?
Comportar-se é um dos populares Python Frameworks de teste BDD. Veja como o Behave funciona:
- Os arquivos de funcionalidades são escritos pelo seu Analista de Negócios, Patrocinador ou quem quer que seja o responsável pelos cenários de comportamento. Um arquivo de funcionalidades utiliza uma linguagem natural para descrever uma funcionalidade, ou parte de uma funcionalidade, com exemplos representativos dos resultados esperados.
- Essas etapas do cenário são mapeadas para implementações de etapas escritas em Python.
- Opcionalmente, os controles ambientais executam código antes e depois de etapas, cenários, funcionalidades ou de toda a execução.
Com as funções de cada arquivo definidas, a estrutura pode agora ser instalada.
Configurando o BDD Testing Framework Comporte-se Windows
Instalação:
- Baixe e instale Python 3 de https://www.python.org/
- Execute o seguinte comando no prompt de comando para instalar o Behave.
pip install behave- AQUI: PyCharm Aqui é utilizada a Community Edition — https://www.jetbrains.com/pycharm/download/
Configuração do projeto:
- Crie um novo projeto
- Crie a seguinte estrutura de diretórios.
A captura de tela acima mostra o layout que o Behave espera: um características diretório contendo os arquivos de recursos e um diretório aninhado passos diretório contendo o Python implementações. O Behave descobre ambas pelo nome, portanto os nomes das pastas devem corresponder exatamente.
Arquivos de recursos:
Agora vamos criar o arquivo de recursos. Sample_REST_API_Testing.feature, com a funcionalidade sendo operações CRUD no serviço 'posts'.
Este exemplo usa o https://jsonplaceholder.typicode.com/ Este é um serviço REST de exemplo, uma API falsa e gratuita que aceita solicitações de escrita e retorna respostas realistas sem persistir as alterações.
Exemplo de cenário POST
Scenario: POST post example -> creating a new post item using the 'posts' service Given I set post posts API endpoint -> prerequisite: sets the URL of the posts service When I set HEADER param request content type as "application/json" And set request body And send POST HTTP request -> the actual test step Then I receive valid HTTP response code 201 And Response body "POST" is non-empty -> verification of the response body
Da mesma forma, você pode escrever os cenários restantes da seguinte maneira:
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"
Implementação de etapas
Agora, para as etapas de funcionalidade usadas nos cenários acima, você pode escrever implementações em Python arquivos dentro do diretório “steps”.
O framework Behave identifica a função de degrau comparando os decoradores com o predicado do arquivo de recursos. Por exemplo, um Dado Em um cenário de arquivo de recursos, o predicado busca uma função degrau que contenha o @given decorador. A mesma correspondência ocorre para Ao e EntãoNo caso de 'Mas' e 'E', a função degrau recebe o mesmo decorador do degrau precedente. Por exemplo, se 'E' segue um Dado, o decorador da função degrau correspondente é @given.
Por exemplo, a etapa "Quando" para POST pode ser implementada da seguinte forma. Observe como "application/json" é passado do arquivo de recursos para o espaço reservado entre chaves — isso é chamado de parametrização.
# Decorator: the braced name captures a value from the feature file @when(u'I Set HEADER param request content type as "{header_content_type}"') def step_impl(context, header_content_type): # Step implementation: set the content type on the request header request_headers['Content-Type'] = header_content_type
Da mesma forma, a implementação das outras etapas na etapa Python O arquivo terá a seguinte aparência:
sample_step_implementation.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 = 'https://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_content_type}"') def step_impl(context, header_content_type): request_headers['Content-Type'] = header_content_type # "And" or "But" steps are renamed by behave to match their preceding step @when(u'Set request Body') def step_impl(context): request_bodies['POST'] = {"title": "foo", "body": "bar", "userId": "1"} @when(u'Send POST HTTP request') def step_impl(context): # send the request and save the response object response = requests.post(url=api_endpoints['POST_URL'], json=request_bodies['POST'], headers=request_headers) response_texts['POST'] = response.text print("post response :" + response.text) response_codes['POST'] = response.status_code @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'] == 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']) @when(u'Send GET HTTP request') def step_impl(context): response = requests.get(url=api_endpoints['GET_URL'], headers=request_headers) response_texts['GET'] = response.text response_codes['GET'] = response.status_code @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] == 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): response = requests.put(url=api_endpoints['PUT_URL'], json=request_bodies['PUT'], headers=request_headers) response_texts['PUT'] = response.text print("update response :" + response.text) response_codes['PUT'] = response.status_code # 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): response = requests.delete(url=api_endpoints['DELETE_URL']) response_texts['DELETE'] = response.text print("DELETE response :" + response.text) response_codes['DELETE'] = response.status_code # END DELETE
⚠️ Nota: Comparando códigos de status com == em vez de is assuntos. O is O operador testa a identidade do objeto, não a igualdade, e Python 3.8 e versões posteriores aumentam um Aviso de sintaxe: “is” com um literal para esse padrão. Qualquer afirmação escrita como assert code is 201 deve ser reescrito como assert code == 201.
Executando os testes
O desenvolvimento do script de teste está concluído, então vamos executar os testes. Execute o seguinte comando no prompt de comando para executar o arquivo de funcionalidade:
:: Run a single feature file with the pretty formatter behave -f pretty features\feature_files_folder\Sample_REST_API_Testing.feature :: Run every feature in the project behave
Isso exibe os resultados da execução do teste da seguinte forma:
Exibição de relatório no console
A saída no console é conveniente durante o desenvolvimento, mas as partes interessadas geralmente preferem um relatório legível, que o Allure fornece.
Relatórios
Primeiro, instale o formatador Allure Behave e a ferramenta de linha de comando Allure. O formatador é um Python pacote; a ferramenta de linha de comando é um download separado descrito no Documentação do Relatório Allure.
:: 1. Install the formatter pip install allure-behave :: 2. Run the tests and write raw results to a folder behave -f allure_behave.formatter:AllureFormatter -o reports/allure-results features/ :: 3. Render and open the HTML report allure serve reports/allure-results
Isso gera o relatório de resultados dos testes em um formato apresentável e informativo como este:
Relatório de teste em formato HTML
Relatório de teste exibindo o resultado do cenário individual
Um conjunto de funcionalidades só é útil se permanecer legível à medida que a API cresce, e é aí que a disciplina na elaboração de arquivos de funcionalidades se mostra vantajosa.
Melhores práticas para escrever arquivos de recursos de comportamento
Um conjunto de testes Behave se degrada rapidamente quando os cenários descrevem cliques e payloads em vez de comportamentos. As práticas abaixo mantêm os arquivos de funcionalidades legíveis para as partes interessadas do negócio, ao mesmo tempo que preservam a funcionalidade.ping da Python Camada de fácil manutenção para engenheiros.
- Descreva o comportamento, não a implementação. Escreva "Dado que um cliente possui saldo suficiente", e não "Dado que a coluna de saldo é igual a 500". O arquivo de funcionalidades descreve o que o sistema faz; o arquivo de etapas descreve como isso é verificado.
- Mantenha um comportamento por cenário. Um cenário que verifica um código de status, um corpo de resposta e uma linha de banco de dados é, na verdade, três cenários distintos. Ao separá-los, as falhas são identificadas diretamente como causa.
- Mova a configuração compartilhada para segundo plano. O exemplo acima usa
Background: Given I set sample REST API urlpara que cada cenário herde a base URL Sem repetir. - Use parâmetros em vez de duplicar. Marcadores de posição entre chaves, como
"{request_name}"Permita que uma única função de etapa atenda às asserções GET, PUT e DELETE, razão pela qual o exemplo precisa de muito menos definições de etapas do que os cenários. - Utilize o Modelo de Cenário para variações de dados. Quando o mesmo comportamento precisa ser comprovado para várias entradas, um
Examples:A tabela é mais clara do que os cenários copiados. - Evite dependências entre cenários. O Behave não garante que um cenário POST seja executado antes de um cenário GET em todas as configurações. Cada cenário deve criar o estado necessário.
- Substitua as variáveis globais de nível de módulo pelo contexto. O exemplo armazena endpoints e respostas em dicionários de módulos para maior concisão. Em ambientes de produção, armazene-os no diretório do Behave.
contextobjeto para que o estado seja redefinido corretamente entre os cenários. - Cenários de marcação para execuções seletivas. Etiquetas como
@smokeor@regressionpermitirbehave --tags=@smoke, o que mantém o feedback do pipeline rápido.
Aplicar esses hábitos desde o primeiro arquivo de funcionalidades mantém um conjunto de ferramentas BDD valioso muito além dos exemplos CRUD iniciais. Equipes que expandem essa abordagem frequentemente a combinam com Cucumber para projetos JVM ou Postman para verificações exploratórias de API.







