Propriedades do Flow Test Engine

Índice

Estrutura e Configuração

Variáveis e Interpolação

Validação e Assertions

Input e Interação

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)

PropriedadeTipoObrigatórioDescriçãoExemplo
node_idstring✅ SimIdentificador único da suíte (kebab-case)"user-auth-test"
suite_namestring✅ SimNome descritivo da suíte de testes"User Authentication Tests"
descriptionstring❌ NãoDescrição detalhada da suíte"Complete auth flow testing"
base_urlstring❌ NãoURL base para requests relativos"{{api_base_url}}" ou "https://api.example.com"
execution_mode"sequential" | "parallel"❌ NãoModo de execução (padrão: sequential)"sequential"
variablesRecord<string, any>❌ NãoVariáveis locais da suíte{user_id: 123, api_key: "abc"}
exportsstring[]❌ NãoVariáveis exportadas globalmente["auth_token", "user_id"]
exports_optionalstring[]❌ NãoExports opcionais (sem warnings)["optional_var"]
dependsFlowDependency[]❌ NãoDependências entre suítesVer seção de dependências
stepsTestStep[]✅ SimArray de passos de testeVer seção de steps
metadataobject❌ NãoMetadados da suíteVer seção de metadata

1.1 Metadata da Suite

PropriedadeTipoDescriçãoValores Aceitos
prioritystringNível de prioridade"critical", "high", "medium", "low"
tagsstring[]Tags para categorização["smoke", "regression", "auth"]
timeoutnumberTimeout geral em ms30000 (30 segundos)
estimated_duration_msnumberDuração estimada em ms5000
requires_user_inputbooleanIndica se requer input interativotrue ou false

1.2 Flow Dependency (FlowDependency)

PropriedadeTipoObrigatórioDescriçãoExemplo
pathstringCondicional*Caminho para o arquivo de dependência"./auth/login.yaml" ou "common/setup.yaml"
path_type"relative" | "absolute"❌ NãoTipo de resolução de caminho (padrão: relative)"relative" ou "absolute"
node_idstringCondicional*ID do nó para referência direta"auth-setup"
requiredboolean❌ NãoSe a dependência é obrigatóriatrue
cacheboolean | number❌ NãoCache: true, false ou TTL em segundostrue ou 300 (5 min)
conditionstring❌ NãoCondição JMESPath para execução"environment == 'test'"
variablesRecord<string, any>❌ NãoVariáveis para sobrescrever{test_mode: true}
retryobject❌ NãoConfiguração de retry{max_attempts: 3, delay_ms: 1000}

Nota: Deve fornecer path OU node_id, não ambos.


2. Propriedades dos Steps (TestStep)

PropriedadeTipoObrigatórioDescriçãoExemplo
namestring✅ SimNome descritivo do passo"Login user"
step_idstring❌ NãoIdentificador único do passo"login-step"
requestRequestDetailsCondicional*Configuração da requisição HTTPVer seção Request
assertAssertions❌ NãoRegras de validaçãoVer seção Assertions
captureRecord<string, string>❌ NãoExtração de dados (JMESPath){token: "body.access_token"}
inputInputConfig | InputConfig[]❌ NãoEntrada interativa do usuárioVer seção Input
callStepCallConfig❌ NãoChamada cross-suiteVer seção Call
iterateIterationConfig❌ NãoExecução em loopVer seção Iterate
scenariosConditionalScenario[]❌ NãoCenários condicionaisVer seção Scenarios
continue_on_failureboolean❌ NãoContinuar se o passo falhartrue ou false
metadataTestStepMetadata❌ NãoMetadados do passoVer seção Metadata

Nota: request é opcional apenas se o step contém apenas input ou call.

2.1 Request Details (RequestDetails)

PropriedadeTipoObrigatórioDescriçãoValores/Exemplo
methodstring✅ SimMétodo HTTP"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"
urlstring✅ SimURL (absoluta ou relativa)"/api/users" ou "https://api.example.com/users"
headersRecord<string, string>❌ NãoCabeçalhos HTTP{"Content-Type": "application/json", "Authorization": "Bearer {{token}}"}
bodyany❌ NãoCorpo da requisição (POST/PUT/PATCH){name: "John", email: "john@example.com"}
paramsRecord<string, any>❌ NãoParâmetros de query string{page: 1, limit: 10}
timeoutnumber❌ NãoTimeout da requisição em ms30000

2.2 Step Metadata (TestStepMetadata)

PropriedadeTipoDescriçãoExemplo
prioritystringPrioridade do passo"critical", "high", "medium", "low"
tagsstring[]Tags para categorização["api", "validation"]
timeoutnumberTimeout do passo em ms10000
retryobjectConfiguração de retry{max_attempts: 3, delay_ms: 1000}
depends_onstring[]IDs de steps que devem executar antes["setup-step", "auth-step"]
descriptionstringDescrição do que o passo faz"Creates user account"
skipstringCondição para pular o step"{{environment}} !== 'prod'"

3. Sistema de Interpolação de Variáveis

3.1 Sintaxe e Fontes

TipoSintaxeDescriçãoExemplo
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

CategoriaExemplos de MétodosResultado
$faker.personfirstName, lastName, fullNameNomes de pessoas
$faker.internetemail, url, userName, passwordDados de internet
$faker.phonenumber, imeiNúmeros de telefone
$faker.locationcity, country, streetAddressDados geográficos
$faker.commerceproductName, price, departmentDados comerciais
$faker.companyname, catchPhrase, bsDados de empresa
$faker.stringuuid, alphanumeric, numericStrings especiais
$faker.numberint, float, binaryNúmeros
$faker.datepast, future, recent, soonDatas
$faker.loremword, words, sentence, paragraphTexto lorem ipsum

3.3 JavaScript Expressions

Caso de UsoExpressãoResultado
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

PropriedadeTipoDescriçãoExemplo
status_codenumber | AssertionChecksValidação de código HTTP200 ou {equals: 200}
bodyRecord<string, AssertionChecks>Validações do corpo da respostaVer tabela de operadores
headersRecord<string, AssertionChecks>Validações de headers{"content-type": {contains: "json"}}
response_time_msobjectValidação de tempo de resposta{less_than: 2000, greater_than: 10}
customArray<CustomAssertion>Asserções customizadasVer seção Custom

4.2 Operadores de Asserção (AssertionChecks)

OperadorTipoDescriçãoExemploUso
equalsanyIgualdade exata{equals: 200}Comparação de valores
not_equalsanyDiferença{not_equals: null}Verificar que não é igual
containsanyContém substring/valor{contains: "success"}Strings, arrays
not_containsanyNão contém{not_contains: "error"}Strings, arrays
greater_thannumberMaior que{greater_than: 0}Comparação numérica
less_thannumberMenor que{less_than: 1000}Comparação numérica
greater_than_or_equalnumberMaior ou igual{greater_than_or_equal: 1}Comparação numérica
less_than_or_equalnumberMenor ou igual{less_than_or_equal: 100}Comparação numérica
regexstringPadrão regex{regex: "^[a-z]+$"}Validação de formato
patternstringAlias para regex{pattern: "\\d{3}"}Validação de formato
existsbooleanCampo existe{exists: true}Verificar presença
not_existsbooleanCampo não existe{exists: false}Verificar ausência
typestringTipo do valor{type: "string"}Validação de tipo
lengthobjectValidação de tamanho{length: {equals: 5}}Arrays, strings
minLengthnumberTamanho mínimo{minLength: 3}Arrays, strings
notEmptybooleanNão vazio{notEmpty: true}Qualquer tipo
inany[]Valor está na lista{in: ["active", "pending"]}Verificar inclusão
not_inany[]Valor não está na lista{not_in: ["deleted", "banned"]}Verificar exclusão

4.3 Tipos Suportados

TipoDescriçãoExemplo de Valor
"string"Texto"Hello World"
"number"Número (int ou float)123, 45.67
"boolean"Booleanotrue, false
"array"Lista[1, 2, 3]
"object"Objeto{key: "value"}
"null"Valor nulonull

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

PropriedadeTipoObrigatórioDescrição
namestring✅ SimNome da asserção
conditionstring✅ SimExpressão JMESPath ou JavaScript
messagestring❌ NãoMensagem 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. Extração de Dados - Capture (JMESPath)

5.1 Sintaxe Básica

PadrãoDescriçãoExemplo ExpressionResultado
Campo simplesAcesso diretobody.tokenValor de response.body.token
Campo aninhadoNavegação profundabody.user.profile.nameValor aninhado
Array por índiceAcesso posicionalbody.items[0]Primeiro item
Array - último itemÍndice negativobody.items[-1]Último item
ProjeçãoMapear arraybody.data[*].idArray de todos os IDs
Resposta completaCapturar tudo@Objeto de resposta inteiro
Status codeCódigo HTTPstatus ou status_code200, 404, etc.
HeadersCabeçalhosheaders.Content-TypeValor do header

5.2 Operações Avançadas JMESPath

OperaçãoSintaxeDescriçãoExemplo
Filtroarray[?condition]Filtrar elementositems[?status=='active']
Pipeexpression | functionAplicar funçãoitems | length(@)
Multi-select{key1: expr1, key2: expr2}Criar objeto{id: body.id, name: body.name}
Flattenarray[]Achatar array aninhadocategories[].products[]
Sortsort_by(array, &field)Ordenar por camposort_by(items, &price)
Containscontains(haystack, needle)Verificar inclusãocontains(tags, 'urgent')
Lengthlength(array)Contar elementoslength(items)
Max/Minmax(array) / min(array)Valor máximo/mínimomax(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, &timestamp)"

  # Resposta completa para debug
  full_response: "@"

6. Input Interativo (InputConfig)

6.1 Propriedades Base

PropriedadeTipoObrigatórioDescriçãoExemplo
promptstring✅ SimMensagem exibida ao usuário"Enter your API key:"
variablestring✅ SimNome da variável para armazenar"api_key"
typestring✅ SimTipo de inputVer tabela de tipos
descriptionstring❌ NãoDescrição detalhada"API key for authentication"
defaultany❌ NãoValor padrão"default_value"
placeholderstring❌ NãoTexto placeholder"Enter value here..."
requiredboolean❌ NãoSe o input é obrigatóriotrue
stylestring❌ NãoEstilo visual"simple", "boxed", "highlighted"
timeout_secondsnumber❌ NãoTimeout antes de usar default30
conditionstring❌ NãoCondição JMESPath para exibir"{{status}} == 200"
ci_defaultany❌ NãoValor em ambientes CI/CD"ci_value"
validationInputValidationConfig❌ NãoRegras de validaçãoVer seção de validação
dynamicInputDynamicConfig❌ NãoProcessamento dinâmicoVer seção dinâmica
optionsArray | stringCondicional*Opções para select/multiselectVer seção de options

Nota: options é obrigatório para tipos select e multiselect.

6.2 Tipos de Input

TipoDescriçãoUse CaseExemplo
textEntrada de texto simplesNomes, IDs"Enter username"
passwordTexto mascaradoSenhas, tokens"Enter API key"
numberApenas númerosQuantidades, IDs"Enter user ID"
emailEmail com validaçãoEndereços de email"Enter email address"
urlURL com validaçãoLinks, endpoints"Enter API endpoint"
selectSeleção únicaEscolher uma opção"Select environment"
multiselectSeleção múltiplaEscolher várias opções"Select features"
confirmConfirmação sim/nãoAções destrutivas"Delete user?"
multilineTexto multilinhaDescriçõ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}}}"

6.4 Input Validation

PropriedadeTipoDescriçãoExemplo
min_lengthnumberTamanho mínimo5
max_lengthnumberTamanho máximo100
patternstringRegex de validação"^[a-zA-Z0-9]+$"
minnumber | stringValor mínimo (números)0
maxnumber | stringValor máximo (números)100
optionsArray | stringValores permitidos[{value: "opt1"}]
custom_validationstringJavaScript customizado"value.length > 3"
expressionsArrayValidações dinâmicasVer seção expressions

6.5 Input Dynamic Config

PropriedadeTipoDescrição
scope"runtime" | "suite" | "global"Escopo padrão das variáveis derivadas
captureRecord<string, string>Capturas JMESPath do contexto
computedRecord<string, string>Variáveis computadas (JavaScript)
reevaluateDynamicVariableDefinition[]Definições para reavaliação
exportsstring[]Variáveis para exportar globalmente
persist_to_globalbooleanPersistir todas derivadas no registry global

7. Iteração (IterationConfig)

7.1 Array Iteration

PropriedadeTipoObrigatórioDescriçãoExemplo
overstring✅ SimExpressão para o array"{{test_users}}"
asstring✅ SimNome 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

PropriedadeTipoObrigatórioDescriçãoExemplo
rangestring✅ SimRange no formato "start..end""1..10"
asstring✅ SimNome 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ávelTipoDescrição
{{as_variable}}anyValor atual (item ou índice)
_iteration.indexnumberÍndice atual (0-based)
_iteration.isFirstbooleanSe é a primeira iteração
_iteration.isLastbooleanSe é a última iteração

8. Cenários Condicionais (ConditionalScenario)

8.1 Estrutura

PropriedadeTipoObrigatórioDescrição
namestring❌ NãoNome descritivo do cenário
conditionstring✅ SimExpressão JMESPath
thenobject❌ NãoAções se condição verdadeira
elseobject❌ NãoAções se condição falsa

8.2 Ações em Then/Else

PropriedadeTipoDescrição
assertAssertionsAsserções adicionais
captureRecord<string, string>Capturas condicionais
variablesRecord<string, any>Variáveis estáticas a definir
stepsTestStep[]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

OperadorDescriçãoExemplo
==Igualdadestatus_code == \200“
!=Diferençabody.status != 'error'
<, <=, >, >=Comparaçãobody.count > \10“
&&E lógicostatus_code == \200` && body.success`
||OU lógicostatus_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

PropriedadeTipoObrigatórioDescriçãoExemplo
teststring✅ SimCaminho para o arquivo alvo"../auth/login.yaml" ou "common/setup.yaml"
path_type"relative" | "absolute"❌ NãoTipo de resolução (padrão: relative)"relative"
stepstring✅ SimID ou nome do step a chamar"login-step"
variablesRecord<string, any>❌ NãoVariáveis para injetar{username: "test", password: "pass"}
isolate_contextboolean❌ NãoIsolar contexto (padrão: true)true
on_error"fail" | "continue" | "warn"❌ NãoEstratégia de erro (padrão: fail)"fail"
timeoutnumber❌ NãoTimeout em ms30000
retryobject❌ NãoConfig 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_contextComportamento
true (padrão)Variáveis capturadas ficam em namespace {{step-id.variable}}
falseVariáveis capturadas mesclam diretamente no escopo do chamador

10. Tipos de Dados e Valores Especiais

10.1 Tipos Primitivos

TipoDescriçãoExemplo YAMLInterpolação
stringTextoname: "John""{{user_name}}"
numberInteiro ou floatage: 30, price: 19.99{{user_id}}
booleanVerdadeiro/falsoactive: true{{is_active}}
nullValor nulodeleted_at: null-
arrayListatags: ["a", "b"]{{tags}}
objectObjeto aninhadouser: {id: 1, name: "John"}{{user}}

10.2 Valores Especiais em Contexto de Resposta

CaminhoDescriçãoTipoExemplo
status ou status_codeCódigo HTTPnumber200, 404
headersCabeçalhos HTTPobject{"content-type": "application/json"}
bodyCorpo da respostaany{success: true, data: [...]}
@Resposta completaobject{status: 200, headers: {...}, body: {...}}

11. Configuração de Retry

PropriedadeTipoDescriçãoExemplo
max_attemptsnumberNúmero máximo de tentativas3
delay_msnumberDelay entre tentativas em ms1000
# 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ívelPropriedadeEscopoPadrão
Globalflow-test.config.yml: timeoutTodas requisiçõesConfigurável
Suitemetadata.timeoutToda a suiteSem limite
Stepmetadata.timeoutStep específicoHerda da suite
Requestrequest.timeoutRequisição HTTP individual30000 ms (30s)
Inputinput.timeout_secondsInput interativoSem limite
Callcall.timeoutChamada cross-suiteHerda do step

13. Resolução de URLs

Tipo de URLBase URLResultado
Relativa (/api/users)https://api.example.comhttps://api.example.com/api/users
Relativa sem / (users)https://api.example.comhttps://api.example.com/users
Absoluta (https://other.com/api)Qualquerhttps://other.com/api (ignora base_url)
Com protocolo (http://)QualquerURL absoluta (ignora base_url)

14. Escopo de Variáveis

EscopoDescriçãoAcessoPersistência
Local (Suite)Definidas em variablesApenas na suite atualDuração da suite
RuntimeCapturadas durante execuçãoSteps seguintes na mesma suiteDuração da suite
Global (Exported)Listadas em exportsTodas as suites (via {{suite-id.var}})Toda a execução
EnvironmentSistema operacionalTodas as suites (via {{$env.VAR}})Permanente
DynamicDe inputs com dynamic configConforme scope configuradoConforme scope

14.1 Ordem de Precedência (maior para menor)

  1. Variáveis runtime (capturadas no step atual)
  2. Variáveis de iteração (as variable)
  3. Variáveis locais da suite
  4. Variáveis exportadas de dependencies
  5. Variáveis globais (de exports)
  6. 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

ModeDescriçãoUse CaseRestrições
sequentialSteps executam em ordemFluxos com dependênciasNenhuma
parallelSteps executam simultaneamenteTestes independentesNão suporta input interativo

17. Priority Levels

NívelDescriçãoUso Típico
criticalTestes críticosSmoke tests, funcionalidades core
highAlta prioridadeFuncionalidades importantes
mediumPrioridade médiaTestes de regression padrão
lowBaixa prioridadeTestes edge cases, opcionais

Pode filtrar execução por prioridade via CLI:

npx flow-test-engine --priority critical,high

18. Tags

Tags são strings arbitrárias para categorização. Exemplos comuns:

TagPropósito
smokeTestes de smoke
regressionSuite de regressão
authTestes de autenticação
apiTestes de API
e2eTestes end-to-end
integrationTestes de integração
performanceTestes de performance
securityTestes de segurança

Filtrar por tags:

npx flow-test-engine --tags smoke,auth

19. Exemplos Práticos Completos

19.1 Autenticação com Captura e Uso de Token

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}}"}

19.2 Iteração com Dados Dinâmicos

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

19.4 Input Interativo com Validação

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

19.6 Testes de Performance com Response Time

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(@)"

19.7 Validação Complexa com Regex e Tipos

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

ProblemaCausa ProvávelSolução
Variable {{var}} not foundVariável não definida ou capturadaVerificar variables ou capture anterior
JMESPath evaluation failedExpressão inválidaTestar em https://jmespath.org/
URL resolution errorBase URL incorretoVerificar base_url e se URL é relativa/absoluta
Assertion failed: status_codeStatus inesperadoAdicionar scenarios para lidar com múltiplos status
Timeout exceededRequisição muito lentaAumentar timeout ou otimizar API
Circular dependency detectedLoop em dependsRevisar cadeia de dependências
Input timeoutUsuário não respondeuDefinir ci_default e timeout_seconds
Iteration failedArray vazio ou inválidoValidar 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çãoDescriçãoWorkaround
Input em parallelinput só funciona em modo sequentialUsar execution_mode: sequential
Variável de iteraçãoas variable só disponível no escopo do iterateCapturar antes do fim da iteração
Cross-suite variablesRequer que suite seja executada antesUsar depends para garantir ordem
JMESPath limitationsNão suporta todas as features de JavaScriptUsar {{$js:}} para lógica complexa
Timeout granularNão há timeout por assertion individualUsar timeout no step ou request
Parallel com dependênciasDependências entre steps em parallel não são garantidasUsar 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

Flow Test Engine
© 2025 Flow Test Engine. Dynamic Reporting System.