---
title: "Métodos de Teste do `test::nginx`: Configuração, Envio de Solicitações e Tratamento de Respostas - API7.ai"
description: "Este artigo descreve como usar o test::nginx para enviar solicitações e lidar com respostas em um caso de teste."
url: "https://api7.ai/pt/learning-center/openresty/all-inclusive-testing-methods-test-nginx"
---

# Métodos de Teste do \`test::nginx\`: Configuração, Envio de Solicitações e Tratamento de Respostas

API7.ai

November 18, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

No artigo anterior, já tivemos o primeiro vislumbre do `test::nginx` e executamos o exemplo mais simples. No entanto, em um projeto de código aberto real, os casos de teste escritos em `test::nginx` são muito mais complexos e difíceis de dominar do que o código de exemplo. Caso contrário, não seria chamado de obstáculo.

Neste artigo, vou guiá-lo pelos comandos e métodos de teste mais frequentemente usados no `test::nginx`, para que você possa entender a maioria dos conjuntos de casos de teste no projeto OpenResty e tenha a capacidade de escrever casos de teste mais realistas. Mesmo que você ainda não tenha contribuído com código para o OpenResty, familiarizar-se com o framework de testes do OpenResty será uma grande inspiração para você projetar e escrever casos de teste em seu trabalho.

O teste `test::nginx` essencialmente gera um `nginx.conf` e inicia um processo NGINX com base na configuração de cada caso de teste. Em seguida, ele simula uma solicitação de cliente com o corpo e os cabeçalhos especificados. Depois, o código Lua no caso de teste processa a solicitação e faz uma resposta. Nesse momento, o `test::nginx` analisa informações críticas como o corpo da resposta, os cabeçalhos da resposta e os logs de erro e os compara com a configuração do teste. Se houver uma discrepância, o teste falha com um erro; caso contrário, é bem-sucedido.

O `test::nginx` fornece muitos primitivos DSL (Linguagem Específica de Domínio). Fiz uma classificação simples de acordo com a configuração do NGINX, o envio de solicitações, o processamento de respostas e a verificação de logs. Esses 20% da funcionalidade podem cobrir 80% dos cenários de aplicação, por isso devemos ter um domínio firme sobre eles. Quanto a outros primitivos e usos mais avançados, vamos apresentá-los no próximo artigo.

## Configuração do NGINX

Vamos primeiro olhar para a configuração do NGINX. O primitivo do `test::nginx` com a palavra-chave "config" está relacionado à configuração do NGINX, como `config`, `stream_config`, `http_config`, etc.

Suas funções são as mesmas: inserir a configuração especificada do NGINX em diferentes contextos do NGINX. Essas configurações podem ser comandos do NGINX ou código Lua encapsulado em `content_by_lua_block`.

Ao fazer testes de unidade, `config` é o primitivo mais comumente usado, no qual carregamos bibliotecas Lua e chamamos funções para testes de caixa branca. Aqui está um trecho de código de teste, que não pode ser executado completamente. Ele é de um projeto de código aberto real, então, se você estiver interessado, pode clicar no [link](https://github.com/apache/apisix/blob/master/t/plugin/key-auth.t#L11) para ver o teste completo, ou pode tentar executá-lo localmente.

```
=== TEST 1: sanity
--- config
    location /t {
        content_by_lua_block {
            local plugin = require("apisix.plugins.key-auth")
            local ok, err = plugin.check_schema({key = 'test-key'})
            if not ok then
                ngx.say(err)
            end
            ngx.say("done")
        }
    }
```

O objetivo deste caso de teste é verificar se a função `check_schema` no arquivo de código `plugins.key-auth` funciona corretamente. Ele usa o comando NGINX `content_by_lua_block` em `location /t` para requerer o módulo a ser testado e chamar diretamente a função que precisa ser verificada.

Este é um meio comum de teste de caixa branca no `test::nginx`. No entanto, essa configuração sozinha não é suficiente para completar o teste, então vamos continuar e ver como enviar uma solicitação de cliente.

## Enviando Solicitações

Simular um cliente enviando uma solicitação envolve vários detalhes, então vamos começar com o mais simples - enviar uma única solicitação.

### `request`

Continuando com o caso de teste acima, se quisermos que o código de teste de unidade seja executado, então temos que iniciar uma solicitação HTTP para o endereço `/t` especificado na configuração, como mostrado no seguinte código de teste:

```
--- request
GET /t
```

Este código envia uma solicitação `GET` para `/t` no primitivo de solicitação. Aqui, não especificamos o endereço IP, domínio ou porta de acesso, nem especificamos se é `HTTP 1.0` ou `HTTP 1.1`. Todos esses detalhes são ocultados pelo `test::nginx`, então não precisamos nos preocupar. Este é um dos benefícios do DSL - precisamos apenas nos concentrar na lógica de negócios sem nos distrair com todos os detalhes.

Além disso, isso fornece flexibilidade parcial. Por exemplo, o padrão é o protocolo para `HTTP 1.1`, ou se quisermos testar `HTTP 1.0`, podemos especificar separadamente:

```
--- request
GET /t  HTTP/1.0
```

Além do método `GET`, o método `POST` também precisa ser suportado. No exemplo a seguir, podemos `POST` a string `hello world` para o endereço especificado.

```
--- request
POST /t  
hello world
```

Novamente, o `test::nginx` calcula o comprimento do corpo da solicitação para você aqui e adiciona os cabeçalhos de solicitação `host` e `connection` para garantir que esta seja uma solicitação normal automaticamente.

Claro, podemos adicionar comentários para torná-lo mais legível. Aqueles que começam com `#` serão reconhecidos como comentários de código.

```
--- request
# post request
POST /t  
hello world
```

A solicitação também suporta um modo mais complexo e flexível, que usa `eval` como um filtro para incorporar código Perl diretamente, já que o `test::nginx` é escrito em Perl. Se a linguagem DSL atual não atender às suas necessidades, `eval` é a "arma definitiva" para executar código Perl diretamente.

Para o uso de `eval`, vamos ver alguns exemplos simples aqui, e continuaremos com outros mais complexos no próximo artigo.

```
--- request eval
"POST /t
hello\x00\x01\x02
world\x03\x04\xff"
```

No primeiro exemplo, usamos `eval` para especificar caracteres não imprimíveis, que é um de seus usos. O conteúdo entre as aspas duplas será tratado como uma string Perl e então passado para o `request` como um argumento.

Aqui está um exemplo mais interessante:

```
--- request eval
"POST /t\n" . "a" x 1024
```

No entanto, para entender este exemplo, precisamos saber algo sobre strings em Perl, então preciso mencionar brevemente dois pontos aqui.

- Em Perl, usamos um ponto para representar a concatenação de strings. Isso não é um pouco semelhante aos dois pontos do `Lua`?
- Um `x` minúsculo indica o número de vezes que um caractere é repetido. Por exemplo, o `"a" x 1024` acima significa que o caractere "a" é repetido 1024 vezes.

Portanto, o segundo exemplo significa que o método `POST` envia uma solicitação contendo `1024` caracteres `a` para o endereço `/t`.

### `pipelined_requests`

Depois de entender como enviar uma única solicitação, vamos ver como enviar várias solicitações. No `test::nginx`, podemos usar o primitivo `pipelined_requests` para enviar várias solicitações em sequência dentro da mesma conexão `keep-alive`:

```
--- pipelined_requests eval
["GET /hello", "GET /world", "GET /foo", "GET /bar"]
```

Por exemplo, este exemplo acessará essas quatro APIs sequencialmente na mesma conexão. Há duas vantagens sobre isso:

- A primeira é que muito código de teste repetitivo pode ser eliminado, e os quatro casos de teste podem ser comprimidos em um.
- A segunda e mais importante razão é que podemos usar solicitações em pipeline para detectar se a lógica do código terá exceções no caso de múltiplos acessos.

Você pode se perguntar, se eu escrever vários casos de teste em sequência, então o código também será executado várias vezes na fase de execução. Isso não cobre o segundo problema acima?

Isso se resume ao modo de execução do `test::nginx`, que funciona de maneira diferente do que você pode pensar. Após cada caso de teste, o `test::nginx` encerra o processo NGINX atual, e todos os dados na memória desaparecem. Ao executar o próximo caso de teste, o `nginx.conf` é regenerado, e um novo `Worker` do NGINX é iniciado. Esse mecanismo garante que os casos de teste não se afetem.

Portanto, quando queremos testar várias solicitações, precisamos usar o primitivo `pipelined_requests`. Com base nele, podemos simular limitação de taxa, limitação de concorrência e muitos outros cenários para testar se o seu sistema funciona corretamente com cenários mais realistas e complexos. Vamos deixar isso para o próximo artigo também, pois envolverá vários comandos e primitivos.

### `repeat_each`

Acabamos de mencionar o caso de testar várias solicitações, então como devemos executar o mesmo teste várias vezes?

Para esse problema, o `test::nginx` fornece uma configuração global: `repeat_each`, que é uma função Perl que por padrão é `repeat_each(1)`, indicando que o caso de teste será executado apenas uma vez. Portanto, nos casos de teste anteriores, não nos preocupamos em configurá-lo separadamente.

Naturalmente, podemos configurá-lo antes da função `run_test()`, por exemplo, alterando o argumento para `2`.

```
repeat_each(2);
run_tests();
```

Então, cada caso de teste é executado duas vezes, e assim por diante.

### `more_headers`

Depois de falar sobre o corpo da solicitação, vamos olhar para os cabeçalhos da solicitação. Como mencionamos acima, o `test::nginx` envia a solicitação com os cabeçalhos `host` e `connection` por padrão. E os outros cabeçalhos da solicitação?

`more_headers` é especificamente projetado para fazer isso.

```
--- more_headers
X-Foo: blah
```

Podemos usá-lo para definir vários cabeçalhos personalizados. Se quisermos definir mais de um cabeçalho, então definimos mais de uma linha:

```
--- more_headers
X-Foo: 3
User-Agent: openresty
```

## Processando Respostas

Depois de enviar a solicitação, a parte mais importante do `test::nginx` é processar a resposta, onde determinaremos se a resposta atende às expectativas. Aqui dividimos isso em quatro partes e as apresentamos: o corpo da resposta, o cabeçalho da resposta, o código de status da resposta e o log.

### `response_body`

O contraparte do primitivo de solicitação é `response_body`, e o seguinte é um exemplo de suas duas configurações em uso:

```
=== TEST 1: sanity
--- config
    location /t {
        content_by_lua_block {
            ngx.say("hello")
        }
    }
--- request
GET /t
--- response_body
hello
```

Este caso de teste passará se o corpo da resposta for `hello`, e reportará um erro em outros casos. Mas como testamos um corpo de retorno longo? Não se preocupe, o `test::nginx` já cuidou disso para você. Ele suporta a detecção do corpo da resposta com uma expressão regular, como a seguinte:

```
--- response_body_like
^he\w+$
```

Isso permite que você seja muito flexível com o corpo da resposta. Além disso, o `test::nginx` também suporta operações `unlike`:

```
--- response_body_unlike
^he\w+$
```

Neste ponto, se o corpo da resposta for `hello`, o teste não passará.

Na mesma linha, após entender a detecção de uma única solicitação, vamos olhar para a detecção de várias solicitações. Aqui está um exemplo de como usá-lo com `pipelined_requests`:

```
--- pipelined_requests eval
["GET /hello", "GET /world", "GET /foo", "GET /bar"]
--- response_body eval
["hello", "world", "oo", "bar"]
```

Claro, o importante a notar aqui é que, quantas solicitações você enviar, você precisará ter tantas respostas para corresponder.

### `response_headers`

Em segundo lugar, vamos falar sobre o cabeçalho da resposta. O cabeçalho da resposta é semelhante ao cabeçalho da solicitação, onde cada linha corresponde à chave e ao valor de um cabeçalho.

```
--- response_headers
X-RateLimit-Limit: 2
X-RateLimit-Remaining: 1
```

Como a detecção do corpo da resposta, os cabeçalhos da resposta também suportam expressões regulares e operações `unlike`, como `response_headers_like`, `raw_response_headers_like` e `raw_response_headers_unlike`.

### `error_code`

O terceiro é o código de resposta. A detecção do código de resposta suporta comparação direta e também suporta operações `like`, como os dois exemplos a seguir:

```
--- error_code: 302
```

```
--- error_code_like: ^(?:500)?$
```

No caso de várias solicitações, o `error_code` precisa ser verificado várias vezes:

```
--- pipelined_requests eval
["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
--- error_code eval
[200, 200, 503, 503]
```

### `error_log`

O último item de teste é o log de erro. Na maioria dos casos de teste, nenhum log de erro é gerado. Podemos usar `no_error_log` para detectar:

```
--- no_error_log
[error]
```

No exemplo acima, se a string `[error]` aparecer no `error.log` do NGINX, o teste falhará. Este é um recurso muito comum, e é recomendável que você adicione a detecção do log de erro a todos os seus testes normais.

```
--- error_log
hello world
```

A configuração acima está detectando a presença de `hello world` no `error.log`. Claro, você pode usar `eval` incorporado em código Perl para implementar a detecção de expressão regular, como o seguinte:

```
--- error_log eval
qr/\[notice\] .*?  \d+ hello world/
```

## Resumo

Hoje, estamos aprendendo como enviar solicitações e testar respostas no `test::nginx`, contendo o corpo da solicitação, o cabeçalho, o código de status da resposta e o log de erro. Podemos implementar um conjunto completo de casos de teste com a combinação desses primitivos.

Finalmente, aqui está uma questão para reflexão: Quais são as vantagens e desvantagens do `test::nginx`, uma DSL abstrata? Sinta-se à vontade para deixar comentários e discutir comigo, e você também é bem-vindo a compartilhar este artigo para comunicar e pensar juntos.

Share article link

Share

Related Posts

[![OpenResty FAQ | Estrutura de Rede para Testes, Recursos Relacionados a SSL, DSL, Ferramenta \`ab\`](https://static.api7.ai/2022/12/02/63896c177fde1.png)](https://api7.ai/learning-center/openresty/faq-3)

## [OpenResty FAQ | Estrutura de Rede para Testes, Recursos Relacionados a SSL, DSL, Ferramenta \`ab\`](https://api7.ai/learning-center/openresty/faq-3)

[![Uso Pouco Conhecido do \`test::nginx\`](https://static.api7.ai/2022/11/25/63802a7fb4697.png)](https://api7.ai/learning-center/openresty/little-known-usage-of-test-nginx)

## [Uso Pouco Conhecido do \`test::nginx\`](https://api7.ai/learning-center/openresty/little-known-usage-of-test-nginx)

Table of Contents

- [Configuração do NGINX](#configura%C3%A7%C3%A3o-do-nginx)

- [Enviando Solicitações](#enviando-solicita%C3%A7%C3%B5es)

  - [`request`](#request)
  - [`pipelined_requests`](#pipelined_requests)
  - [`repeat_each`](#repeat_each)
  - [`more_headers`](#more_headers)

- [Processando Respostas](#processando-respostas)

  - [`response_body`](#response_body)
  - [`response_headers`](#response_headers)
  - [`error_code`](#error_code)
  - [`error_log`](#error_log)

- [Resumo](#resumo)
