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.

  • 🗣️ Princípio fundamental: O Desenvolvimento Orientado a Comportamento (BDD) amplia o Desenvolvimento Orientado a Testes (TDD) ao expressar cada cenário em linguagem natural, que pessoas sem conhecimento de programação podem ler e aprovar.
  • 🧱 Estrutura do cenário: Dado estabelece as pré-condições, Quando realiza a ação e Então afirma o resultado, enquanto E ou Mas herdam o decorador da etapa anterior.
  • 🌐 Cobertura REST: O exemplo demonstra o comportamento CRUD completo por meio de POST, GET, PUT e DELETE no serviço público de posts jsonplaceholder.
  • 🐍 Configuração do Framework: O Behave é instalado através do pip em Python 3 e espera um diretório de recursos contendo arquivos de recursos, além de um pacote de etapas contendo as implementações.
  • 🔗 Ligação por etapas: Decoradores como @given, @when e @then correspondem ao texto da funcionalidade, e marcadores entre chaves passam valores do cenário para o texto. Python função.
  • ▶ ️ Execução: O formatador intuitivo imprime um resultado de aprovação ou reprovação com código de cores para cada etapa no console.
  • 📊 Relatório: O formatador allure-behave gera resultados legíveis por máquina que a linha de comando do Allure renderiza como um relatório HTML navegável.

O que é o teste BDD?

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:

  1. A transferência de fundos deve ser efetuada se houver saldo suficiente na conta de origem.
  2. A transferência de fundos deve ocorrer se os detalhes do ar-condicionado de destino estiverem corretos
  3. 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.
  4. A transferência de fundos deve ser efetuada mesmo em feriados bancários.
  5. 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:

Configuração do projeto:

  • Crie um novo projeto
  • Crie a seguinte estrutura de diretórios.

Configuração do Projeto

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:

Configuração do Projeto

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:

Implementação de etapas

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:

Executando os testes

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órios

Relatório de teste em formato HTML

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.

  1. 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.
  2. 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.
  3. Mova a configuração compartilhada para segundo plano. O exemplo acima usa Background: Given I set sample REST API url para que cada cenário herde a base URL Sem repetir.
  4. 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.
  5. 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.
  6. 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.
  7. 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. context objeto para que o estado seja redefinido corretamente entre os cenários.
  8. Cenários de marcação para execuções seletivas. Etiquetas como @smoke or @regression permitir behave --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.

Perguntas Frequentes

Ambos leem arquivos de recursos Gherkin. Cucumber vincula etapas a JavaRuby, ou JavaScript, enquanto Behave os vincula a PythonEscolha aquela que corresponde à sua pilha de aplicações para que as definições de passos reutilizem as ferramentas de teste existentes.

Sim. O Behave retorna um código de saída diferente de zero quando qualquer cenário falha, então JenkinsO GitLab CI ou o GitHub Actions podem interromper a compilação automaticamente. Publique a pasta de resultados do Allure como um artefato de compilação para geração de relatórios.

Sim. Travas deslizantes portáteis AI Os assistentes elaboram cenários "Dado-Quando-Então" a partir de histórias de usuário ou de uma especificação OpenAPI. RevVeja a saída para casos negativos ausentes e regras de negócio, pois os cenários gerados geralmente abrangem apenas o caminho feliz.

As ferramentas de IA detectam cenários duplicados ou quase duplicados, sugerem etapas parametrizadas reutilizáveis ​​e agrupam falhas intermitentes por causa provável. Isso reduz o esforço de auditoria manual à medida que os arquivos de funcionalidades se multiplicam entre as equipes.

Obtenha o token uma única vez no gancho `before_all` do arquivo `environment.py`, armazene-o no objeto de contexto e anexe-o como um cabeçalho de Autorização em cada etapa da requisição. Mantenha as credenciais em variáveis ​​de ambiente, nunca em arquivos de funcionalidade.

Resuma esta postagem com: