Propriedades do Flow Test Engine
Índice
Estrutura e Configuração
Variáveis e Interpolação
Validação e Assertions
Controle de Fluxo
Configurações Avançadas
Exemplos e Troubleshooting
Descrição
Este documento descreve o processo de documentação abrangente de todas as propriedades técnicas do projeto Flow Test Engine, um motor de testes de API baseado em YAML que executa flows declarativos com recursos avançados de automação, relatórios e integração.
Objetivo
Criar uma documentação técnica completa que cubra todas as propriedades, tipos de dados, capacidades de interpolação, operadores, e funcionalidades do Flow Test Engine, facilitando o desenvolvimento, manutenção e uso correto do framework.
Contexto do Projeto
- Framework: Flow Test Engine (TypeScript/Node.js)
- Propósito: Motor de testes de API com execução declarativa via YAML
- Arquitetura: Services modulares com suporte a interpolação avançada, asserções, captura de dados e relatórios
- Formato Principal: YAML para definição de suites de teste
Referência Completa de Propriedades YAML
1. Estrutura Base do YAML (TestSuite)
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
node_id | string | ✅ Sim | Identificador único da suíte (kebab-case) | "user-auth-test" |
suite_name | string | ✅ Sim | Nome descritivo da suíte de testes | "User Authentication Tests" |
description | string | ❌ Não | Descrição detalhada da suíte | "Complete auth flow testing" |
base_url | string | ❌ Não | URL base para requests relativos | "{{api_base_url}}" ou "https://api.example.com" |
execution_mode | "sequential" | "parallel" | ❌ Não | Modo de execução (padrão: sequential) | "sequential" |
variables | Record<string, any> | ❌ Não | Variáveis locais da suíte | {user_id: 123, api_key: "abc"} |
exports | string[] | ❌ Não | Variáveis exportadas globalmente | ["auth_token", "user_id"] |
exports_optional | string[] | ❌ Não | Exports opcionais (sem warnings) | ["optional_var"] |
depends | FlowDependency[] | ❌ Não | Dependências entre suítes | Ver seção de dependências |
steps | TestStep[] | ✅ Sim | Array de passos de teste | Ver seção de steps |
metadata | object | ❌ Não | Metadados da suíte | Ver seção de metadata |
| Propriedade | Tipo | Descrição | Valores Aceitos |
|---|
priority | string | Nível de prioridade | "critical", "high", "medium", "low" |
tags | string[] | Tags para categorização | ["smoke", "regression", "auth"] |
timeout | number | Timeout geral em ms | 30000 (30 segundos) |
estimated_duration_ms | number | Duração estimada em ms | 5000 |
requires_user_input | boolean | Indica se requer input interativo | true ou false |
1.2 Flow Dependency (FlowDependency)
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
path | string | Condicional* | Caminho para o arquivo de dependência | "./auth/login.yaml" ou "common/setup.yaml" |
path_type | "relative" | "absolute" | ❌ Não | Tipo de resolução de caminho (padrão: relative) | "relative" ou "absolute" |
node_id | string | Condicional* | ID do nó para referência direta | "auth-setup" |
required | boolean | ❌ Não | Se a dependência é obrigatória | true |
cache | boolean | number | ❌ Não | Cache: true, false ou TTL em segundos | true ou 300 (5 min) |
condition | string | ❌ Não | Condição JMESPath para execução | "environment == 'test'" |
variables | Record<string, any> | ❌ Não | Variáveis para sobrescrever | {test_mode: true} |
retry | object | ❌ Não | Configuração de retry | {max_attempts: 3, delay_ms: 1000} |
Nota: Deve fornecer path OU node_id, não ambos.
2. Propriedades dos Steps (TestStep)
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
name | string | ✅ Sim | Nome descritivo do passo | "Login user" |
step_id | string | ❌ Não | Identificador único do passo | "login-step" |
request | RequestDetails | Condicional* | Configuração da requisição HTTP | Ver seção Request |
assert | Assertions | ❌ Não | Regras de validação | Ver seção Assertions |
capture | Record<string, string> | ❌ Não | Extração de dados (JMESPath) | {token: "body.access_token"} |
input | InputConfig | InputConfig[] | ❌ Não | Entrada interativa do usuário | Ver seção Input |
call | StepCallConfig | ❌ Não | Chamada cross-suite | Ver seção Call |
iterate | IterationConfig | ❌ Não | Execução em loop | Ver seção Iterate |
scenarios | ConditionalScenario[] | ❌ Não | Cenários condicionais | Ver seção Scenarios |
continue_on_failure | boolean | ❌ Não | Continuar se o passo falhar | true ou false |
metadata | TestStepMetadata | ❌ Não | Metadados do passo | Ver seção Metadata |
Nota: request é opcional apenas se o step contém apenas input ou call.
2.1 Request Details (RequestDetails)
| Propriedade | Tipo | Obrigatório | Descrição | Valores/Exemplo |
|---|
method | string | ✅ Sim | Método HTTP | "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS" |
url | string | ✅ Sim | URL (absoluta ou relativa) | "/api/users" ou "https://api.example.com/users" |
headers | Record<string, string> | ❌ Não | Cabeçalhos HTTP | {"Content-Type": "application/json", "Authorization": "Bearer {{token}}"} |
body | any | ❌ Não | Corpo da requisição (POST/PUT/PATCH) | {name: "John", email: "john@example.com"} |
params | Record<string, any> | ❌ Não | Parâmetros de query string | {page: 1, limit: 10} |
timeout | number | ❌ Não | Timeout da requisição em ms | 30000 |
| Propriedade | Tipo | Descrição | Exemplo |
|---|
priority | string | Prioridade do passo | "critical", "high", "medium", "low" |
tags | string[] | Tags para categorização | ["api", "validation"] |
timeout | number | Timeout do passo em ms | 10000 |
retry | object | Configuração de retry | {max_attempts: 3, delay_ms: 1000} |
depends_on | string[] | IDs de steps que devem executar antes | ["setup-step", "auth-step"] |
description | string | Descrição do que o passo faz | "Creates user account" |
skip | string | Condição para pular o step | "{{environment}} !== 'prod'" |
3. Sistema de Interpolação de Variáveis
3.1 Sintaxe e Fontes
| Tipo | Sintaxe | Descrição | Exemplo |
|---|
| Variável básica | {{variable}} | Variável local ou capturada | {{user_id}}, {{auth_token}} |
| Variável aninhada | {{object.field}} | Acesso a campos aninhados | {{user.profile.name}} |
| Variável de array | {{array[index]}} | Acesso por índice | {{items[0].id}} |
| Variável global | {{suite-id.variable}} | Variável exportada de outra suite | {{auth-login.auth_token}} |
| Ambiente | {{$env.VAR_NAME}} | Variável de ambiente (prefixo FLOW_TEST_) | {{$env.API_KEY}} → lê FLOW_TEST_API_KEY |
| Faker.js | {{$faker.category.method}} | Dados fake dinâmicos | {{$faker.internet.email}}, {{$faker.person.firstName}} |
| JavaScript | {{$js:expression}} | Expressão JavaScript | {{$js:Date.now()}}, {{$js:Math.random()}} |
3.2 Faker.js - Categorias Principais
| Categoria | Exemplos de Métodos | Resultado |
|---|
$faker.person | firstName, lastName, fullName | Nomes de pessoas |
$faker.internet | email, url, userName, password | Dados de internet |
$faker.phone | number, imei | Números de telefone |
$faker.location | city, country, streetAddress | Dados geográficos |
$faker.commerce | productName, price, department | Dados comerciais |
$faker.company | name, catchPhrase, bs | Dados de empresa |
$faker.string | uuid, alphanumeric, numeric | Strings especiais |
$faker.number | int, float, binary | Números |
$faker.date | past, future, recent, soon | Datas |
$faker.lorem | word, words, sentence, paragraph | Texto lorem ipsum |
3.3 JavaScript Expressions
| Caso de Uso | Expressão | Resultado |
|---|
| Timestamp atual | {{$js:Date.now()}} | 1704110400000 |
| Data ISO | {{$js:new Date().toISOString()}} | "2024-01-01T12:00:00.000Z" |
| Número aleatório | {{$js:Math.random()}} | 0.123456789 |
| Arredondamento | {{$js:Math.round({{price}} * 1.1)}} | Valor arredondado |
| Base64 encode | {{$js:Buffer.from('text').toString('base64')}} | "dGV4dA==" |
| Cálculo | {{$js:{{quantity}} * {{price}}}} | Resultado do cálculo |
4. Assertions (Validações)
4.1 Estrutura de Assertions
| Propriedade | Tipo | Descrição | Exemplo |
|---|
status_code | number | AssertionChecks | Validação de código HTTP | 200 ou {equals: 200} |
body | Record<string, AssertionChecks> | Validações do corpo da resposta | Ver tabela de operadores |
headers | Record<string, AssertionChecks> | Validações de headers | {"content-type": {contains: "json"}} |
response_time_ms | object | Validação de tempo de resposta | {less_than: 2000, greater_than: 10} |
custom | Array<CustomAssertion> | Asserções customizadas | Ver seção Custom |
4.2 Operadores de Asserção (AssertionChecks)
| Operador | Tipo | Descrição | Exemplo | Uso |
|---|
equals | any | Igualdade exata | {equals: 200} | Comparação de valores |
not_equals | any | Diferença | {not_equals: null} | Verificar que não é igual |
contains | any | Contém substring/valor | {contains: "success"} | Strings, arrays |
not_contains | any | Não contém | {not_contains: "error"} | Strings, arrays |
greater_than | number | Maior que | {greater_than: 0} | Comparação numérica |
less_than | number | Menor que | {less_than: 1000} | Comparação numérica |
greater_than_or_equal | number | Maior ou igual | {greater_than_or_equal: 1} | Comparação numérica |
less_than_or_equal | number | Menor ou igual | {less_than_or_equal: 100} | Comparação numérica |
regex | string | Padrão regex | {regex: "^[a-z]+$"} | Validação de formato |
pattern | string | Alias para regex | {pattern: "\\d{3}"} | Validação de formato |
exists | boolean | Campo existe | {exists: true} | Verificar presença |
not_exists | boolean | Campo não existe | {exists: false} | Verificar ausência |
type | string | Tipo do valor | {type: "string"} | Validação de tipo |
length | object | Validação de tamanho | {length: {equals: 5}} | Arrays, strings |
minLength | number | Tamanho mínimo | {minLength: 3} | Arrays, strings |
notEmpty | boolean | Não vazio | {notEmpty: true} | Qualquer tipo |
in | any[] | Valor está na lista | {in: ["active", "pending"]} | Verificar inclusão |
not_in | any[] | Valor não está na lista | {not_in: ["deleted", "banned"]} | Verificar exclusão |
4.3 Tipos Suportados
| Tipo | Descrição | Exemplo de Valor |
|---|
"string" | Texto | "Hello World" |
"number" | Número (int ou float) | 123, 45.67 |
"boolean" | Booleano | true, false |
"array" | Lista | [1, 2, 3] |
"object" | Objeto | {key: "value"} |
"null" | Valor nulo | null |
4.4 Validação de Length
# Length com operators aninhados
body:
items:
length:
equals: 10 # Exatamente 10 itens
greater_than: 0 # Mais de 0
less_than: 100 # Menos de 100
greater_than_or_equal: 5 # 5 ou mais
less_than_or_equal: 50 # 50 ou menos
4.5 Custom Assertions
| Propriedade | Tipo | Obrigatório | Descrição |
|---|
name | string | ✅ Sim | Nome da asserção |
condition | string | ✅ Sim | Expressão JMESPath ou JavaScript |
message | string | ❌ Não | Mensagem de erro customizada |
custom:
- name: "Valid user ID format"
condition: "body.user.id && typeof body.user.id === 'number'"
message: "User ID must be a number"
5.1 Sintaxe Básica
| Padrão | Descrição | Exemplo Expression | Resultado |
|---|
| Campo simples | Acesso direto | body.token | Valor de response.body.token |
| Campo aninhado | Navegação profunda | body.user.profile.name | Valor aninhado |
| Array por índice | Acesso posicional | body.items[0] | Primeiro item |
| Array - último item | Índice negativo | body.items[-1] | Último item |
| Projeção | Mapear array | body.data[*].id | Array de todos os IDs |
| Resposta completa | Capturar tudo | @ | Objeto de resposta inteiro |
| Status code | Código HTTP | status ou status_code | 200, 404, etc. |
| Headers | Cabeçalhos | headers.Content-Type | Valor do header |
5.2 Operações Avançadas JMESPath
| Operação | Sintaxe | Descrição | Exemplo |
|---|
| Filtro | array[?condition] | Filtrar elementos | items[?status=='active'] |
| Pipe | expression | function | Aplicar função | items | length(@) |
| Multi-select | {key1: expr1, key2: expr2} | Criar objeto | {id: body.id, name: body.name} |
| Flatten | array[] | Achatar array aninhado | categories[].products[] |
| Sort | sort_by(array, &field) | Ordenar por campo | sort_by(items, &price) |
| Contains | contains(haystack, needle) | Verificar inclusão | contains(tags, 'urgent') |
| Length | length(array) | Contar elementos | length(items) |
| Max/Min | max(array) / min(array) | Valor máximo/mínimo | max(prices[*].value) |
5.3 Exemplos Práticos de Capture
capture:
# Simples
token: "body.access_token"
user_id: "body.user.id"
# Arrays
first_item_name: "body.items[0].name"
all_ids: "body.data[*].id"
# Filtros
active_users: "body.users[?status=='active']"
premium_products: "body.products[?price > `100`]"
# Projeções customizadas
user_list: "body.users[*].{id: id, name: name, email: email}"
# Funções
total_items: "length(body.items)"
max_price: "max(body.products[*].price)"
# Ordenação
sorted_by_date: "sort_by(body.events, ×tamp)"
# Resposta completa para debug
full_response: "@"
6.1 Propriedades Base
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
prompt | string | ✅ Sim | Mensagem exibida ao usuário | "Enter your API key:" |
variable | string | ✅ Sim | Nome da variável para armazenar | "api_key" |
type | string | ✅ Sim | Tipo de input | Ver tabela de tipos |
description | string | ❌ Não | Descrição detalhada | "API key for authentication" |
default | any | ❌ Não | Valor padrão | "default_value" |
placeholder | string | ❌ Não | Texto placeholder | "Enter value here..." |
required | boolean | ❌ Não | Se o input é obrigatório | true |
style | string | ❌ Não | Estilo visual | "simple", "boxed", "highlighted" |
timeout_seconds | number | ❌ Não | Timeout antes de usar default | 30 |
condition | string | ❌ Não | Condição JMESPath para exibir | "{{status}} == 200" |
ci_default | any | ❌ Não | Valor em ambientes CI/CD | "ci_value" |
validation | InputValidationConfig | ❌ Não | Regras de validação | Ver seção de validação |
dynamic | InputDynamicConfig | ❌ Não | Processamento dinâmico | Ver seção dinâmica |
options | Array | string | Condicional* | Opções para select/multiselect | Ver seção de options |
Nota: options é obrigatório para tipos select e multiselect.
| Tipo | Descrição | Use Case | Exemplo |
|---|
text | Entrada de texto simples | Nomes, IDs | "Enter username" |
password | Texto mascarado | Senhas, tokens | "Enter API key" |
number | Apenas números | Quantidades, IDs | "Enter user ID" |
email | Email com validação | Endereços de email | "Enter email address" |
url | URL com validação | Links, endpoints | "Enter API endpoint" |
select | Seleção única | Escolher uma opção | "Select environment" |
multiselect | Seleção múltipla | Escolher várias opções | "Select features" |
confirm | Confirmação sim/não | Ações destrutivas | "Delete user?" |
multiline | Texto multilinha | Descrições, JSON | "Enter JSON payload" |
6.3 Options para Select
# Array estático
input:
type: select
options:
- {value: "dev", label: "Development"}
- {value: "prod", label: "Production"}
# De variável capturada
input:
type: select
options: "{{users_list}}" # Deve ser array de objetos
value_path: "id" # Campo para o valor
label_path: "name" # Campo para o label
# Expressão JMESPath
input:
type: select
options: "{{users[*].{value: id, label: name}}}"
| Propriedade | Tipo | Descrição | Exemplo |
|---|
min_length | number | Tamanho mínimo | 5 |
max_length | number | Tamanho máximo | 100 |
pattern | string | Regex de validação | "^[a-zA-Z0-9]+$" |
min | number | string | Valor mínimo (números) | 0 |
max | number | string | Valor máximo (números) | 100 |
options | Array | string | Valores permitidos | [{value: "opt1"}] |
custom_validation | string | JavaScript customizado | "value.length > 3" |
expressions | Array | Validações dinâmicas | Ver seção expressions |
| Propriedade | Tipo | Descrição |
|---|
scope | "runtime" | "suite" | "global" | Escopo padrão das variáveis derivadas |
capture | Record<string, string> | Capturas JMESPath do contexto |
computed | Record<string, string> | Variáveis computadas (JavaScript) |
reevaluate | DynamicVariableDefinition[] | Definições para reavaliação |
exports | string[] | Variáveis para exportar globalmente |
persist_to_global | boolean | Persistir todas derivadas no registry global |
7. Iteração (IterationConfig)
7.1 Array Iteration
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
over | string | ✅ Sim | Expressão para o array | "{{test_users}}" |
as | string | ✅ Sim | Nome da variável do item | "user" |
steps:
- name: "Process user {{user.name}}"
iterate:
over: "{{test_users}}"
as: "user"
request:
method: POST
url: "/users/{{user.id}}"
body:
name: "{{user.name}}"
email: "{{user.email}}"
7.2 Range Iteration
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
range | string | ✅ Sim | Range no formato "start..end" | "1..10" |
as | string | ✅ Sim | Nome da variável do índice | "page" |
steps:
- name: "Test page {{page}}"
iterate:
range: "1..5"
as: "page"
request:
method: GET
url: "/items?page={{page}}"
7.3 Iteration Context (disponível durante iteração)
| Variável | Tipo | Descrição |
|---|
{{as_variable}} | any | Valor atual (item ou índice) |
_iteration.index | number | Índice atual (0-based) |
_iteration.isFirst | boolean | Se é a primeira iteração |
_iteration.isLast | boolean | Se é a última iteração |
8. Cenários Condicionais (ConditionalScenario)
8.1 Estrutura
| Propriedade | Tipo | Obrigatório | Descrição |
|---|
name | string | ❌ Não | Nome descritivo do cenário |
condition | string | ✅ Sim | Expressão JMESPath |
then | object | ❌ Não | Ações se condição verdadeira |
else | object | ❌ Não | Ações se condição falsa |
8.2 Ações em Then/Else
| Propriedade | Tipo | Descrição |
|---|
assert | Assertions | Asserções adicionais |
capture | Record<string, string> | Capturas condicionais |
variables | Record<string, any> | Variáveis estáticas a definir |
steps | TestStep[] | Steps a executar (apenas then) |
scenarios:
- name: "Admin vs Regular User"
condition: "body.user.role == 'admin'"
then:
assert:
body:
permissions: {contains: "admin_access"}
capture:
admin_id: "body.user.id"
variables:
is_admin: true
else:
assert:
status_code: 403
variables:
is_admin: false
- name: "Success path with nested steps"
condition: "status_code == `200`"
then:
steps:
- name: "Verify created resource"
request:
method: GET
url: "/resources/{{body.id}}"
8.3 Operadores em Condições JMESPath
| Operador | Descrição | Exemplo |
|---|
== | Igualdade | status_code == \200“ |
!= | Diferença | body.status != 'error' |
<, <=, >, >= | Comparação | body.count > \10“ |
&& | E lógico | status_code == \200` && body.success` |
|| | OU lógico | status_code == \200` || status_code == `201“ |
! | Negação | !body.error |
Nota: Valores literais em JMESPath devem usar backticks: `200`, 'string'
9. Step Call Cross-Suite (StepCallConfig)
9.1 Propriedades
| Propriedade | Tipo | Obrigatório | Descrição | Exemplo |
|---|
test | string | ✅ Sim | Caminho para o arquivo alvo | "../auth/login.yaml" ou "common/setup.yaml" |
path_type | "relative" | "absolute" | ❌ Não | Tipo de resolução (padrão: relative) | "relative" |
step | string | ✅ Sim | ID ou nome do step a chamar | "login-step" |
variables | Record<string, any> | ❌ Não | Variáveis para injetar | {username: "test", password: "pass"} |
isolate_context | boolean | ❌ Não | Isolar contexto (padrão: true) | true |
on_error | "fail" | "continue" | "warn" | ❌ Não | Estratégia de erro (padrão: fail) | "fail" |
timeout | number | ❌ Não | Timeout em ms | 30000 |
retry | object | ❌ Não | Config de retry | {max_attempts: 3, delay_ms: 1000} |
steps:
- name: "Execute login flow"
call:
test: "../auth/login.yaml"
step: "login-step"
variables:
username: "{{test_user}}"
password: "{{test_password}}"
isolate_context: true
on_error: fail
# Com contexto não-isolado (variáveis mesclam no escopo pai)
- name: "Setup auth"
call:
test: "common/auth-setup.yaml"
path_type: "absolute"
step: "setup"
isolate_context: false
9.2 Comportamento de Isolamento
isolate_context | Comportamento |
|---|
true (padrão) | Variáveis capturadas ficam em namespace {{step-id.variable}} |
false | Variáveis capturadas mesclam diretamente no escopo do chamador |
10. Tipos de Dados e Valores Especiais
10.1 Tipos Primitivos
| Tipo | Descrição | Exemplo YAML | Interpolação |
|---|
string | Texto | name: "John" | "{{user_name}}" |
number | Inteiro ou float | age: 30, price: 19.99 | {{user_id}} |
boolean | Verdadeiro/falso | active: true | {{is_active}} |
null | Valor nulo | deleted_at: null | - |
array | Lista | tags: ["a", "b"] | {{tags}} |
object | Objeto aninhado | user: {id: 1, name: "John"} | {{user}} |
10.2 Valores Especiais em Contexto de Resposta
| Caminho | Descrição | Tipo | Exemplo |
|---|
status ou status_code | Código HTTP | number | 200, 404 |
headers | Cabeçalhos HTTP | object | {"content-type": "application/json"} |
body | Corpo da resposta | any | {success: true, data: [...]} |
@ | Resposta completa | object | {status: 200, headers: {...}, body: {...}} |
11. Configuração de Retry
| Propriedade | Tipo | Descrição | Exemplo |
|---|
max_attempts | number | Número máximo de tentativas | 3 |
delay_ms | number | Delay entre tentativas em ms | 1000 |
# No nível de metadata do step
metadata:
retry:
max_attempts: 3
delay_ms: 1000
# No nível de dependency
depends:
- path: "./setup.yaml"
retry:
max_attempts: 2
delay_ms: 500
# No nível de call
call:
test: "./external.yaml"
step: "api-call"
retry:
max_attempts: 5
delay_ms: 2000
12. Timeouts
| Nível | Propriedade | Escopo | Padrão |
|---|
| Global | flow-test.config.yml: timeout | Todas requisições | Configurável |
| Suite | metadata.timeout | Toda a suite | Sem limite |
| Step | metadata.timeout | Step específico | Herda da suite |
| Request | request.timeout | Requisição HTTP individual | 30000 ms (30s) |
| Input | input.timeout_seconds | Input interativo | Sem limite |
| Call | call.timeout | Chamada cross-suite | Herda do step |
13. Resolução de URLs
| Tipo de URL | Base URL | Resultado |
|---|
Relativa (/api/users) | https://api.example.com | https://api.example.com/api/users |
Relativa sem / (users) | https://api.example.com | https://api.example.com/users |
Absoluta (https://other.com/api) | Qualquer | https://other.com/api (ignora base_url) |
Com protocolo (http://) | Qualquer | URL absoluta (ignora base_url) |
14. Escopo de Variáveis
| Escopo | Descrição | Acesso | Persistência |
|---|
| Local (Suite) | Definidas em variables | Apenas na suite atual | Duração da suite |
| Runtime | Capturadas durante execução | Steps seguintes na mesma suite | Duração da suite |
| Global (Exported) | Listadas em exports | Todas as suites (via {{suite-id.var}}) | Toda a execução |
| Environment | Sistema operacional | Todas as suites (via {{$env.VAR}}) | Permanente |
| Dynamic | De inputs com dynamic config | Conforme scope configurado | Conforme scope |
14.1 Ordem de Precedência (maior para menor)
- Variáveis runtime (capturadas no step atual)
- Variáveis de iteração (
as variable)
- Variáveis locais da suite
- Variáveis exportadas de dependencies
- Variáveis globais (de
exports)
- Variáveis de ambiente
15. Response Object Structure
Estrutura do objeto de resposta disponível para captures e assertions:
{
status: number, // 200, 404, etc.
status_code: number, // alias para status
headers: Record<string, string>, // {"content-type": "application/json"}
body: any, // Corpo parseado (JSON) ou string
data: any, // Alias para body (axios)
response_time_ms: number // Tempo de resposta em milissegundos
}
16. Execution Modes
| Mode | Descrição | Use Case | Restrições |
|---|
sequential | Steps executam em ordem | Fluxos com dependências | Nenhuma |
parallel | Steps executam simultaneamente | Testes independentes | Não suporta input interativo |
17. Priority Levels
| Nível | Descrição | Uso Típico |
|---|
critical | Testes críticos | Smoke tests, funcionalidades core |
high | Alta prioridade | Funcionalidades importantes |
medium | Prioridade média | Testes de regression padrão |
low | Baixa prioridade | Testes edge cases, opcionais |
Pode filtrar execução por prioridade via CLI:
npx flow-test-engine --priority critical,high
Tags são strings arbitrárias para categorização. Exemplos comuns:
| Tag | Propósito |
|---|
smoke | Testes de smoke |
regression | Suite de regressão |
auth | Testes de autenticação |
api | Testes de API |
e2e | Testes end-to-end |
integration | Testes de integração |
performance | Testes de performance |
security | Testes de segurança |
Filtrar por tags:
npx flow-test-engine --tags smoke,auth
19. Exemplos Práticos Completos
suite_name: "Authentication Flow"
node_id: "auth-flow"
base_url: "{{api_base_url}}"
variables:
username: "{{$env.TEST_USERNAME}}"
password: "{{$env.TEST_PASSWORD}}"
exports: ["auth_token", "user_id"]
steps:
# Login e captura de token
- name: "User login"
step_id: "login"
request:
method: POST
url: "/auth/login"
headers:
Content-Type: "application/json"
body:
username: "{{username}}"
password: "{{password}}"
assert:
status_code: 200
body:
token: {exists: true, type: "string"}
user:
id: {exists: true, type: "number"}
capture:
auth_token: "body.token"
user_id: "body.user.id"
token_expiry: "body.expires_at"
# Usar token capturado
- name: "Get user profile"
request:
method: GET
url: "/users/{{user_id}}"
headers:
Authorization: "Bearer {{auth_token}}"
assert:
status_code: 200
body:
id: {equals: "{{user_id}}"}
suite_name: "Bulk User Creation"
node_id: "bulk-users"
base_url: "{{api_base_url}}"
variables:
users_to_create:
- {name: "Alice", role: "admin"}
- {name: "Bob", role: "user"}
- {name: "Charlie", role: "moderator"}
exports: ["created_user_ids"]
steps:
- name: "Create user: {{user.name}}"
iterate:
over: "{{users_to_create}}"
as: "user"
request:
method: POST
url: "/users"
body:
name: "{{user.name}}"
email: "{{$faker.internet.email}}"
role: "{{user.role}}"
created_at: "{{$js:new Date().toISOString()}}"
assert:
status_code: 201
body:
name: {equals: "{{user.name}}"}
role: {equals: "{{user.role}}"}
capture:
"created_user_{{_iteration.index}}": "body.id"
19.3 Cenários Condicionais Avançados
suite_name: "Conditional API Testing"
node_id: "conditional-test"
base_url: "{{api_base_url}}"
steps:
- name: "Create resource"
request:
method: POST
url: "/resources"
body:
name: "Test Resource"
type: "document"
capture:
response_status: "status"
resource_id: "body.id"
resource_exists: "body.already_exists"
scenarios:
# Recurso criado com sucesso
- name: "New resource created"
condition: "response_status == `201` && !resource_exists"
then:
assert:
body:
id: {exists: true, type: "string"}
created_at: {exists: true}
capture:
new_resource_id: "body.id"
steps:
- name: "Verify new resource"
request:
method: GET
url: "/resources/{{new_resource_id}}"
# Recurso já existe
- name: "Resource already exists"
condition: "response_status == `409` || resource_exists"
then:
variables:
use_existing: true
capture:
existing_resource_id: "body.existing_id"
steps:
- name: "Update existing resource"
request:
method: PUT
url: "/resources/{{existing_resource_id}}"
body:
name: "Updated Resource"
# Erro genérico
- name: "Error occurred"
condition: "response_status >= `400` && response_status != `409`"
then:
assert:
body:
error: {exists: true}
message: {exists: true, type: "string"}
variables:
creation_failed: true
suite_name: "Interactive API Testing"
node_id: "interactive-test"
base_url: "{{api_base_url}}"
execution_mode: "sequential"
metadata:
requires_user_input: true
steps:
# Buscar opções da API
- name: "Get available environments"
request:
method: GET
url: "/environments"
capture:
environments_list: "body.environments"
default_env: "body.default"
# Input com opções dinâmicas
- name: "Select environment"
input:
prompt: "Select the target environment:"
variable: "selected_environment"
type: "select"
options: "{{environments_list[*].{value: id, label: name}}}"
default: "{{default_env}}"
ci_default: "dev"
required: true
style: "boxed"
validation:
expressions:
- name: "Valid environment"
expression: "contains(environments_list[*].id, selected_environment)"
message: "Selected environment must be valid"
severity: "error"
# Input de texto com validação
- name: "Enter API key"
input:
prompt: "Enter API key for {{selected_environment}}:"
variable: "api_key"
type: "password"
required: true
validation:
min_length: 20
pattern: "^sk-[a-zA-Z0-9_-]+$"
custom_validation: "value.startsWith('sk-')"
timeout_seconds: 60
ci_default: "{{$env.TEST_API_KEY}}"
# Usar valores do input
- name: "Test API connection"
request:
method: GET
url: "/test-connection"
headers:
X-Environment: "{{selected_environment}}"
Authorization: "Bearer {{api_key}}"
assert:
status_code: 200
body:
environment: {equals: "{{selected_environment}}"}
authenticated: {equals: true}
19.5 Dependências e Reuso entre Suites
File: auth-setup.yaml
suite_name: "Authentication Setup"
node_id: "auth-setup"
base_url: "{{api_base_url}}"
variables:
test_user: "test@example.com"
test_password: "SecurePass123!"
exports: ["auth_token", "user_id", "session_id"]
steps:
- name: "Setup test user"
step_id: "setup"
request:
method: POST
url: "/auth/login"
body:
email: "{{test_user}}"
password: "{{test_password}}"
assert:
status_code: 200
capture:
auth_token: "body.access_token"
user_id: "body.user.id"
session_id: "body.session_id"
File: main-test.yaml
suite_name: "Main API Tests"
node_id: "main-test"
base_url: "{{api_base_url}}"
# Depende do setup de autenticação
depends:
- path: "./auth-setup.yaml"
required: true
cache: 300 # Cache por 5 minutos
retry:
max_attempts: 2
delay_ms: 1000
steps:
# Usa token da dependency
- name: "Create protected resource"
request:
method: POST
url: "/api/resources"
headers:
Authorization: "Bearer {{auth-setup.auth_token}}"
body:
name: "Protected Resource"
owner_id: "{{auth-setup.user_id}}"
assert:
status_code: 201
suite_name: "Performance Tests"
node_id: "performance-test"
base_url: "{{api_base_url}}"
metadata:
priority: "high"
tags: ["performance", "sla"]
steps:
# Teste de endpoint rápido
- name: "Health check performance"
request:
method: GET
url: "/health"
timeout: 5000
assert:
status_code: 200
response_time_ms:
less_than: 100 # SLA: < 100ms
greater_than: 0
metadata:
retry:
max_attempts: 3
delay_ms: 500
# Teste de paginação
- name: "Pagination performance - Page {{page}}"
iterate:
range: "1..10"
as: "page"
request:
method: GET
url: "/items"
params:
page: "{{page}}"
limit: 50
assert:
status_code: 200
response_time_ms:
less_than: 2000 # SLA: < 2s
body:
items:
length:
less_than_or_equal: 50
greater_than: 0
capture:
"page_{{page}}_time": "response_time_ms"
"page_{{page}}_count": "body.items | length(@)"
suite_name: "Data Validation Tests"
node_id: "validation-test"
base_url: "{{api_base_url}}"
steps:
- name: "Create user with validation"
request:
method: POST
url: "/users"
body:
username: "{{$faker.internet.userName}}"
email: "{{$faker.internet.email}}"
phone: "{{$faker.phone.number}}"
website: "https://{{$faker.internet.domainName}}"
age: "{{$js:Math.floor(Math.random() * 50) + 18}}"
assert:
status_code: 201
body:
# Validação de email
email:
exists: true
type: "string"
regex: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
notEmpty: true
# Validação de username
username:
exists: true
type: "string"
minLength: 3
regex: "^[a-zA-Z0-9_-]+$"
# Validação de telefone
phone:
exists: true
regex: "^[\\+]?[1-9]?[0-9]{7,15}$"
# Validação de URL
website:
exists: true
regex: "^https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b"
# Validação de idade
age:
exists: true
type: "number"
greater_than_or_equal: 18
less_than: 150
# Validação de ID gerado
id:
exists: true
type: "string"
regex: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" # UUID
# Validação de array de roles
roles:
exists: true
type: "array"
length:
greater_than: 0
less_than: 10
# Validação de timestamp
created_at:
exists: true
type: "string"
regex: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}" # ISO 8601
19.8 Error Handling e Retry
suite_name: "Error Handling Tests"
node_id: "error-handling"
base_url: "{{api_base_url}}"
steps:
# Teste com retry automático
- name: "Flaky endpoint with retry"
request:
method: GET
url: "/flaky-endpoint"
timeout: 5000
assert:
status_code: 200
metadata:
retry:
max_attempts: 5
delay_ms: 2000
timeout: 10000
description: "Endpoint conhecido por ser instável"
# Continue mesmo em falha
- name: "Optional validation"
request:
method: POST
url: "/optional-feature"
body:
feature: "beta_feature"
continue_on_failure: true
assert:
status_code: 200
# Cenários de erro esperados
- name: "Test error responses"
request:
method: POST
url: "/protected-resource"
headers:
Authorization: "Bearer invalid_token"
scenarios:
- name: "Unauthorized access"
condition: "status == `401`"
then:
assert:
status_code: 401
body:
error: {equals: "unauthorized"}
message: {exists: true}
variables:
auth_failed: true
- name: "Forbidden access"
condition: "status == `403`"
then:
assert:
status_code: 403
body:
error: {equals: "forbidden"}
variables:
access_denied: true
20. Guia de Troubleshooting
20.1 Problemas Comuns e Soluções
| Problema | Causa Provável | Solução |
|---|
Variable {{var}} not found | Variável não definida ou capturada | Verificar variables ou capture anterior |
JMESPath evaluation failed | Expressão inválida | Testar em https://jmespath.org/ |
URL resolution error | Base URL incorreto | Verificar base_url e se URL é relativa/absoluta |
Assertion failed: status_code | Status inesperado | Adicionar scenarios para lidar com múltiplos status |
Timeout exceeded | Requisição muito lenta | Aumentar timeout ou otimizar API |
Circular dependency detected | Loop em depends | Revisar cadeia de dependências |
Input timeout | Usuário não respondeu | Definir ci_default e timeout_seconds |
Iteration failed | Array vazio ou inválido | Validar fonte do array antes de iterar |
20.2 Debug Tips
# Capturar resposta completa para debug
capture:
debug_full_response: "@"
debug_status: "status"
debug_headers: "headers"
debug_body: "body"
# Usar variáveis para debug
computed:
debug_timestamp: "{{$js:new Date().toISOString()}}"
debug_random: "{{$js:Math.random()}}"
# Verbose assertions
assert:
custom:
- name: "Debug assertion"
condition: "body"
message: "Body received: {{body}}"
20.3 Validação de YAML
# Verificar sintaxe YAML
npx flow-test-engine --dry-run tests/my-suite.yaml
# Modo verbose para logs detalhados
npx flow-test-engine --verbose tests/my-suite.yaml
# Verificar apenas descoberta
npx flow-test-engine --list
21. Limitações Conhecidas
21.1 Limitações Técnicas
| Limitação | Descrição | Workaround |
|---|
| Input em parallel | input só funciona em modo sequential | Usar execution_mode: sequential |
| Variável de iteração | as variable só disponível no escopo do iterate | Capturar antes do fim da iteração |
| Cross-suite variables | Requer que suite seja executada antes | Usar depends para garantir ordem |
| JMESPath limitations | Não suporta todas as features de JavaScript | Usar {{$js:}} para lógica complexa |
| Timeout granular | Não há timeout por assertion individual | Usar timeout no step ou request |
| Parallel com dependências | Dependências entre steps em parallel não são garantidas | Usar sequential ou separar em suites |
21.2 Boas Práticas
✅ DO:
- Usar
node_id únicos e descritivos
- Definir
ci_default para todos os inputs
- Capturar variáveis essenciais com nomes claros
- Usar
exports apenas para variáveis realmente compartilháveis
- Adicionar
metadata.description para steps complexos
- Validar responses com múltiplas assertions
- Usar
continue_on_failure com cautela
❌ DON’T:
- Usar
execution_mode: parallel com input
- Criar dependências circulares
- Confiar em ordem de execução em parallel
- Capturar dados sensíveis sem necessidade
- Usar timeouts muito curtos em CI/CD
- Ignorar falhas de assertion sem
scenarios
- Misturar lógica de negócio em testes