| 1 | \chapter{Testes}\label{cap:testes}
|
| 2 |
|
| 3 | Delineadas as tecnologias e a arquitetura nos capítulos anteriores, este
|
| 4 | capítulo se debruça como o comportamento do sistema é verificado: quais tipos
|
| 5 | de teste existem, onde são aplicados, o que cada um é capaz --- e incapaz ---
|
| 6 | de detectar, e por que o esforço de verificação foi distribuído da forma como
|
| 7 | foi. O objetivo não é prescrever uma estratégia ideal, mas registrar com
|
| 8 | honestidade o estado da prática no projeto e as suas lacunas, apresentadas de
|
| 9 | forma explícita na Seção~\ref{sec:test-gaps}.
|
| 10 |
|
| 11 | Os dados quantitativos citados ao longo do capítulo, como o número de arquivos
|
| 12 | de teste, referem-se ao estado do repositório em meados de outubro de 2026.
|
| 13 |
|
| 14 | \section{Fundamentos de Teste de Software}\label{sec:tests-basic}
|
| 15 |
|
| 16 | Teste de software é, em sua formulação clássica, a execução de um programa com
|
| 17 | a intenção de revelar defeitos \cite{Myers:2012}. Uma consequência é que um
|
| 18 | conjunto de testes que passa demonstra que os casos exercitados se comportam
|
| 19 | como esperado, mas não necessariamente que o sistema esteja livre de defeitos.
|
| 20 | Essa limitação é relevante para a leitura deste capítulo.
|
| 21 |
|
| 22 | A literatura costuma organizar a atividade de teste em níveis, que se
|
| 23 | distinguem pelo tamanho da parte do sistema sob verificação: o teste de unidade
|
| 24 | exercita a menor parte testável do programa de forma isolada; o teste de
|
| 25 | integração verifica a interação entre partes já testadas; e o teste de sistema
|
| 26 | avalia o sistema completo em condições próximas às de uso. Os nomes e as
|
| 27 | fronteiras entre os níveis variam entre autores; por isso,
|
| 28 | o Quadro~\ref{tab:tests-vocab} fixa o vocabulário usado neste trabalho
|
| 29 | e o relaciona aos termos usuais.
|
| 30 |
|
| 31 | \begin{table}[H]
|
| 32 | \centering
|
| 33 | \caption{Vocabulário de testes adotado neste trabalho}
|
| 34 | \label{tab:tests-vocab}
|
| 35 | \small
|
| 36 | \begin{tabular}{|p{2.8cm}|p{6.6cm}|p{4.2cm}|}
|
| 37 | \hline
|
| 38 | \textbf{Termo} &
|
| 39 | \textbf{O que exercita} &
|
| 40 | \textbf{Nível usual na literatura} \\
|
| 41 |
|
| 42 | \hline
|
| 43 |
|
| 44 | Teste unitário &
|
| 45 | Uma única função ou módulo, sem HTTP e sem banco de dados. &
|
| 46 | Teste de unidade. \\
|
| 47 |
|
| 48 | \hline
|
| 49 |
|
| 50 | Teste de integração &
|
| 51 | A aplicação HTTP real (rotas, validação, autenticação, controladores
|
| 52 | e serviços) sobre um banco de dados real; apenas o que sai do processo
|
| 53 | é simulado &
|
| 54 | Teste de integração, em nível de API. \\
|
| 55 |
|
| 56 | \hline
|
| 57 |
|
| 58 | Teste de \textit{job} &
|
| 59 | Um processador de tarefa em segundo plano, chamado diretamente com uma carga
|
| 60 | fabricada, sobre o banco real. &
|
| 61 | Teste de integração de componente assíncrono \\
|
| 62 |
|
| 63 | \hline
|
| 64 |
|
| 65 | Teste \textit{end-to-end} (E2E) &
|
| 66 | O aplicativo empacotado, em um dispositivo, ao longo de uma jornada real de
|
| 67 | usuário. &
|
| 68 | Teste de sistema. \\
|
| 69 |
|
| 70 | \hline
|
| 71 | \end{tabular}
|
| 72 | \source{Elaborado pelo autor (2026)}
|
| 73 | \end{table}
|
| 74 |
|
| 75 | Outro conceito necessário é o de dublê de teste (\textit{test double}): termo
|
| 76 | genérico para qualquer objeto que substitui uma dependência real durante
|
| 77 | o teste. A taxonomia de \citet{Meszaros:2007} distingue, entre outros,
|
| 78 | \textit{stubs}, \textit{spies}, \textit{mocks} e \textit{fakes}. Neste
|
| 79 | trabalho, o termo \textit{mock} é empregado no sentido amplo adotado pelas
|
| 80 | ferramentas de teste do ecossistema JavaScript --- a substituição de um módulo
|
| 81 | por um substituto controlado ---, e não no sentido restrito da taxonomia.
|
| 82 |
|
| 83 | E então, uma heurística recorrente para distribuir o esforço entre os níveis
|
| 84 | é a chamada pirâmide de testes: muitos testes rápidos e isolados na base, menos
|
| 85 | testes de integração no meio e poucos testes de interface no topo
|
| 86 | \cite{Vocke:2012}. A distribuição observada no Nexo se afasta desse formato:
|
| 87 | alguns testes unitários, maioria de testes de integração e um topo fino,
|
| 88 | predominantemente manual.
|
| 89 |
|
| 90 | \section{Estratégia de Verificação}\label{sec:tests-strategies}
|
| 91 |
|
| 92 | O código-fonte do Nexo é mantido em um único repositório que abriga dois
|
| 93 | projetos independentes: a API e o aplicativo móvel, ambos descritos nos
|
| 94 | capítulos anteriores.
|
| 95 |
|
| 96 | O esforço de teste entre os dois projetos é deliberadamente assimétrico. A API
|
| 97 | possui uma suíte automatizada e é protegida por um portão de integração
|
| 98 | contínua; o aplicativo móvel é verificado majoritariamente de forma manual, com
|
| 99 | uma pequena suíte \textit{end-to-end} e testes unitários por cima. Essa
|
| 100 | assimetria decorre das prioridades do projeto, e não de um descuido, e se
|
| 101 | justifica por, ao menos, dois motivos. O primeiro é que, por limitações de
|
| 102 | força de trabalho, manter uma suíte automatizada completa nas duas esferas
|
| 103 | excederia a capacidade disponível, sendo preferível concentrar o esforço onde
|
| 104 | o retorno é maior. O segundo é que a API é o componente que concentra
|
| 105 | a validação, a autenticação, as regras de negócio e a persistência --- isto é,
|
| 106 | a garantia sobre a integridade dos dados registrados pelo usuário.
|
| 107 |
|
| 108 | A Tabela ~\ref{tab:test-tooling} resume as ferramentas envolvidas e o ponto em
|
| 109 | que cada uma atua.
|
| 110 |
|
| 111 | \begin{table}[H]
|
| 112 | \centering
|
| 113 | \caption{Ferramentas de verificação}
|
| 114 | \label{tab:test-tooling}
|
| 115 | \small
|
| 116 | \begin{tabular}{|p{4.4cm}|p{4.0cm}|p{5.0cm}|}
|
| 117 | \hline
|
| 118 | \textbf{Finalidade} &
|
| 119 | \textbf{Ferramenta} &
|
| 120 | \textbf{Onde atua} \\
|
| 121 |
|
| 122 | \hline
|
| 123 |
|
| 124 | Execução de testes e asserções da API &
|
| 125 | Vitest &
|
| 126 | API \\
|
| 127 |
|
| 128 | \hline
|
| 129 |
|
| 130 | Requisições HTTP nos testes &
|
| 131 | Supertest &
|
| 132 | Dentro dos arquivos do Vitest \\
|
| 133 |
|
| 134 | \hline
|
| 135 |
|
| 136 | Cobertura de código da API &
|
| 137 | Vitest (\textit{coverage}) &
|
| 138 | API, sem limiar mínimo \\
|
| 139 |
|
| 140 | \hline
|
| 141 |
|
| 142 | Banco de dados de teste &
|
| 143 | PostgreSQL &
|
| 144 | \textit{Container} local durante desenvolvimento; serviço efêmero no CI \\
|
| 145 |
|
| 146 | \hline
|
| 147 |
|
| 148 | Testes \textit{end-to-end} móveis &
|
| 149 | Maestro &
|
| 150 | Dispositivo Android físico, via \textit{adb} \\
|
| 151 |
|
| 152 | \hline
|
| 153 |
|
| 154 | \textit{Lint} do aplicativo &
|
| 155 | ESLint (\texttt{expo lint}) &
|
| 156 | Manual, fora do CI \\
|
| 157 |
|
| 158 | \hline
|
| 159 |
|
| 160 | Integração contínua &
|
| 161 | GitHub Actions &
|
| 162 | API \\
|
| 163 |
|
| 164 | \hline
|
| 165 | \end{tabular}
|
| 166 | \source{Elaborado pelo autor (2026)}
|
| 167 | \end{table}
|
| 168 |
|
| 169 | \section{Suíte de Testes da API}\label{sec:test-api}
|
| 170 |
|
| 171 | A suíte automatizada do Nexo concentra-se na API e cobre três frentes
|
| 172 | distintas: os testes unitários, os testes de integração das rotas HTTP e os
|
| 173 | testes dos processadores de tarefas em segundo plano. No momento da redação
|
| 174 | deste capítulo, o diretório \texttt{api/tests} reunia 48 arquivos, dos quais 17
|
| 175 | são unitários, 26 exercitam as rotas e cinco cobrem os \textit{jobs}.
|
| 176 | A execução e as asserções ficam a cargo do Vitest, enquanto as requisições HTTP
|
| 177 | são realizadas pelo Supertest, que permite exercitar a aplicação Express
|
| 178 | diretamente, sem abrir uma porta de rede, porém através de sua pilha HTTP real.
|
| 179 |
|
| 180 | \subsection{Organização e ferramentas}\label{sec:test-api-org}
|
| 181 |
|
| 182 | A configuração do Vitest (\texttt{api/vitest.config.ts}) permite dividir
|
| 183 | a suíte em dois ``projetos'', cada um com condições especiais. O projeto
|
| 184 | \texttt{unit} seleciona os arquivos nomeados com o sufixo
|
| 185 | \texttt{.unit.test.ts} e não carrega configuração global alguma, de modo que
|
| 186 | esses testes dependem apenas das funções que exercitam. Já o projeto
|
| 187 | \texttt{db} seleciona os demais arquivos --- os testes de integração e de
|
| 188 | \textit{job} ---, aplica um arquivo de configuração global e fixa um limite de
|
| 189 | tempo por teste, além de que os testes desse projeto truncam e repovoam o banco
|
| 190 | de dados.
|
| 191 |
|
| 192 | A cobertura de código é apenas mensurada, não imposta. O relatório, produzido
|
| 193 | pela ferramenta de cobertura do Vitest por meio do comando \texttt{npm run
|
| 194 | test:coverage}, não possui limiar mínimo nem bloqueia a integração contínua.
|
| 195 |
|
| 196 | Existe ainda uma verificação estática de tipos (\texttt{npm run typecheck})
|
| 197 | executada no fluxo de CI em caráter consultivo: uma falha é registrada, mas não
|
| 198 | interrompe o processo de \textit{deploy}.
|
| 199 |
|
| 200 | \subsection{Execução e banco de dados de teste}\label{sec:test-api-db}
|
| 201 |
|
| 202 | Os testes que dependem do banco são executados contra uma instância real do
|
| 203 | PostgreSQL --- e não contra um substituto em memória ---, em uma base dedicada
|
| 204 | com o mesmo nome do projeto, adicionando o suffixo \texttt{\_test}, criada por
|
| 205 | um pequeno script e materializada com \texttt{prisma db push}. O isolamento
|
| 206 | entre os casos é obtido por truncamento: antes de cada teste, uma rotina
|
| 207 | executa \texttt{TRUNCATE users CASCADE}, removendo a tabela de usuários e, por
|
| 208 | meio das chaves estrangeiras em cascata, todas as tabelas dependentes. Não há
|
| 209 | \textit{rollback} transacional nem banco efêmero por execução.
|
| 210 |
|
| 211 | O carregamento de dados é feito por funções auxiliares escritas à mão, e não
|
| 212 | por uma biblioteca de fábricas. Para tal, um módulo compartilhado
|
| 213 | (\texttt{api/tests/test-helper.ts}) oferece as primitivas centrais --- criação
|
| 214 | do cliente Prisma, limpeza da base, criação de usuário de teste e geração de
|
| 215 | token ---, enquanto cada arquivo define os \textit{seeders} específicos dos
|
| 216 | registros de que necessita.
|
| 217 |
|
| 218 | A regra que orienta o uso de dublês na API pode ser resumida assim: simula-se
|
| 219 | apenas o que deixa o processo; a aplicação e o banco de dados permanecem reais.
|
| 220 | A camada de filas (BullMQ) é substituída, globalmente, por um substituto que
|
| 221 | registra os \textit{jobs} enfileirados, permitindo que um teste afirme tanto
|
| 222 | a resposta HTTP quanto o trabalho em segundo plano que deveria ter sido
|
| 223 | agendado. De modo semelhante, o envio de arquivos e o cliente de armazenamento
|
| 224 | de objetos são substituídos por implementações controladas, e o disparo de
|
| 225 | e-mail e de notificações \textit{push} é simulado pontualmente, nos arquivos
|
| 226 | que os exercitam.
|
| 227 |
|
| 228 | Duas fronteiras, porém, permanecem deliberadamente reais. O acesso ao banco de
|
| 229 | dados, via Prisma, nunca é simulado; tampouco a autenticação. Os testes geram
|
| 230 | tokens JWT válidos, assinados com o segredo de teste, e os enviam no cabeçalho
|
| 231 | de autorização, de modo que o \textit{middleware} de autenticação real seja
|
| 232 | executado. Essa escolha reduz o risco de que um teste passe contra um
|
| 233 | substituto que se comporte de maneira distinta em produção.
|
| 234 |
|
| 235 | \subsection{Tipos de testes}\label{sec:test-api-tipos}
|
| 236 |
|
| 237 | A suíte da API divide-se em três grupos, que retomam o vocabulário fixado no
|
| 238 | Quadro~\ref{tab:tests-vocab}: os testes unitários, os testes de integração e os
|
| 239 | testes de tarefas assíncronas. Cada grupo é apresentado a seguir com o que
|
| 240 | exercita, o que é capaz de detectar e o que escapa ao seu alcance.
|
| 241 |
|
| 242 | \subsubsection{Testes unitários}
|
| 243 |
|
| 244 | Os testes unitários exercitam uma única função ou módulo, sem HTTP e sem banco
|
| 245 | de dados. Eles concentram-se na lógica pura do sistema, em grande parte
|
| 246 | extraída deliberadamente de seus envoltórios de persistência para se tornar
|
| 247 | testável: os agregadores dos indicadores, os construtores dos relatórios e
|
| 248 | auxiliares compartilhados, como a paginação e a conversão de agendamento.
|
| 249 | Também cobrem o gerador de código de ativação, a validade de tokens, os
|
| 250 | serializadores e as regras de validação. Por serem rápidos e determinísticos,
|
| 251 | constituem o instrumento natural para fixar casos-limite --- fronteiras de
|
| 252 | janelas de tempo, margens de classificação e aritmética de paginação ---, e
|
| 253 | servem como documentação executável dessas regras.
|
| 254 |
|
| 255 | A limitação desse nível é estrutural: ele só alcança a lógica que foi isolada
|
| 256 | em uma costura testável. A maior parte do comportamento do sistema permanece
|
| 257 | fora de seu alcance sem o banco e a camada HTTP, e o nível pouco contribui,
|
| 258 | isoladamente, para a confiança geral na aplicação.
|
| 259 |
|
| 260 | \subsubsection{Testes de integração}
|
| 261 |
|
| 262 | Os testes de integração formam a maior parte da suíte. Cada um constrói a
|
| 263 | aplicação real, conecta um cliente Prisma real e a exercita por meio do
|
| 264 | Supertest, afirmando tanto a resposta HTTP quanto o estado persistido no banco.
|
| 265 | Eles percorrem o caminho completo de uma requisição --- roteamento, validação,
|
| 266 | autenticação, controladores, serviços e persistência --- e, por operarem sobre
|
| 267 | um PostgreSQL real, não sofrem o descolamento que afeta os substitutos: um
|
| 268 | teste não pode passar contra um falso que se comporte de modo diferente da
|
| 269 | produção. Afirmar simultaneamente a resposta e as linhas persistidas verifica o
|
| 270 | efeito da operação, e não apenas o seu valor de retorno.
|
| 271 |
|
| 272 | Em contrapartida, esses testes exigem infraestrutura em execução, são mais
|
| 273 | lentos e, por compartilharem a mesma base, são forçados a rodar em série.
|
| 274 | O isolamento por truncamento é suficiente, porém menos rigoroso do que um
|
| 275 | \textit{rollback} transacional, pois um teste que falha no meio pode deixar
|
| 276 | resíduos. Os dados de teste, ademais, são escritos à mão em cada arquivo, o que
|
| 277 | gera duplicação e o risco de que os \textit{seeders} divirjam entre si ao longo
|
| 278 | do tempo.
|
| 279 |
|
| 280 | \subsubsection{Testes de \textit{tarefas assíncronas}}
|
| 281 |
|
| 282 | Os testes de tarefas assíncronas, ou de \textit{jobs}, importam um processador
|
| 283 | do BullMQ e o chamam diretamente, com uma carga fabricada, contra o banco real.
|
| 284 | Eles cobrem o código que executa fora do ciclo requisição--resposta --- os
|
| 285 | \textit{workers} que geram exportações, enviam mensagens e recalculam
|
| 286 | indicadores ---, chegando, no caso da exportação, a descompactar o anexo
|
| 287 | produzido e inspecionar seu conteúdo.
|
| 288 |
|
| 289 | Como o processador é invocado diretamente, a maquinaria da fila em si ---
|
| 290 | agendamento, repetição, tentativas e ordenação --- permanece fora do alcance
|
| 291 | desses testes. A carga também é fabricada à mão e, por isso, pode divergir
|
| 292 | daquela que o enfileirador realmente envia, sem que nada verifique uma contra
|
| 293 | a outra.
|
| 294 |
|
| 295 | \section{Verificação do Aplicativo Móvel}\label{sec:test-mobile}
|
| 296 |
|
| 297 | Diferentemente da API, o aplicativo móvel não possui uma estrutura automatizada
|
| 298 | de testes em nível de unidade ou de componente. A verificação cotidiana do
|
| 299 | aplicativo é manual, em um dispositivo físico, contra a pilha local em
|
| 300 | execução. Essa escolha é contextual: o aplicativo roda como um \textit{build}
|
| 301 | de desenvolvimento do Expo, ou seja, carrega dependências de código nativo do
|
| 302 | Android.
|
| 303 |
|
| 304 | A verificação manual captura o que os testes automatizados raramente alcançam
|
| 305 | --- problemas visuais, de leiaute e de desempenho no dispositivo ---, mas não
|
| 306 | é repetível, não protege contra regressões e permanece invisível à integração
|
| 307 | contínua: uma tela quebrada pode ser incorporada ao código sem que nada falhe.
|
| 308 |
|
| 309 | \subsection{Suíte \textit{end-to-end} (Maestro)}\label{sec:test-mobile-e2e}
|
| 310 |
|
| 311 | Sobre a verificação manual há uma suíte \textit{end-to-end} opcional, baseada
|
| 312 | no Maestro, uma ferramenta declarativa de automação de interface para
|
| 313 | dispositivos móveis. A suíte cobre uma única jornada central --- login,
|
| 314 | registro de humor e saída --- por meio de três fluxos descritos em YAML,
|
| 315 | marcados como \texttt{smoke} e executados por um pequeno \textit{harness}
|
| 316 | (\texttt{frontend/scripts/e2e.sh}), exposto como \texttt{npm run test:e2e}. Os
|
| 317 | fluxos são dirigidos por identificadores de teste (\texttt{testID}) inseridos
|
| 318 | no código do aplicativo, e não pelo texto visível, o que os torna mais robustos
|
| 319 | a mudanças de interface.
|
| 320 |
|
| 321 | A execução depende de um dispositivo Android físico conectado por \texttt{adb},
|
| 322 | de um \textit{build} de desenvolvimento, da API em funcionamento com um usuário
|
| 323 | de teste previamente semeado e do Metro servindo o aplicativo; não há caminho
|
| 324 | sem cabeça nem integração ao CI, de modo que a suíte é executada manualmente e,
|
| 325 | por consequência, com pouca frequência. Cada fluxo limpa o estado do aplicativo
|
| 326 | e recarrega o \textit{bundle}, o que torna a execução lenta, e a cobertura
|
| 327 | permanece fina: um único caminho feliz, sem casos de erro. A suíte não está,
|
| 328 | ademais, integralmente verde no momento da redação: os fluxos de login e de
|
| 329 | saída passam, mas o fluxo de registro de humor falha ao adicionar um componente
|
| 330 | de humor, porque a tela correspondente é abandonada pelo aplicativo pouco
|
| 331 | depois de ser aberta --- causa ainda não identificada.
|
| 332 |
|
| 333 | \section{Integração Contínua}\label{sec:test-ci}
|
| 334 |
|
| 335 | A integração contínua do Nexo, entendida como a prática de integrar e verificar
|
| 336 | automaticamente as mudanças de código \cite{Humble_Farley_2011}, é executada
|
| 337 | exclusivamente no GitHub Actions e testa somente a API, em linha com
|
| 338 | a estratégia assimétrica já descrita. O fluxo de implantação
|
| 339 | (\texttt{.github/workflows/deploy.yml}) só é executado quando algo acontecem
|
| 340 | mudanças no código da \texttt{api}: sua etapa de teste sobe um serviço efêmero
|
| 341 | de PostgreSQL, instala as dependências, prepara a base de teste, executa
|
| 342 | a verificação de tipos em caráter consultivo e roda a suíte com o Vitest.
|
| 343 | A construção da imagem e a implantação em produção são condicionadas ao sucesso
|
| 344 | dessa etapa, de modo que uma suíte reprovada impede a publicação de uma nova
|
| 345 | versão da API, como demonstrado no Capítulo~\ref{sec:deploy-and-ci}.
|
| 346 |
|
| 347 | O aplicativo móvel, por sua vez, permanece inteiramente fora da integração
|
| 348 | contínua: embora disponha de comandos de \textit{lint} (\texttt{expo lint})
|
| 349 | e de verificação de tipos (\texttt{tsc --noEmit}), ambos são executados apenas
|
| 350 | manualmente, e a suíte \textit{end-to-end} não é disparada por nenhum fluxo.
|
| 351 |
|
| 352 | \section{Cobertura e Lacunas}\label{sec:test-gaps}
|
| 353 |
|
| 354 | Em síntese, a verificação automatizada do Nexo cobre a superfície HTTP
|
| 355 | e a persistência da API, a sua lógica pura e os seus trabalhadores em segundo
|
| 356 | plano; a verificação do aplicativo móvel restringe-se a uma jornada manual de
|
| 357 | fumaça, limitada à plataforma para a qual o produto é de fato distribuído.
|
| 358 | Ficam fora de qualquer cobertura automatizada a lógica do \textit{frontend}
|
| 359 | abaixo do nível \textit{end-to-end}, a maquinaria da fila em si (agendamento,
|
| 360 | repetições e ordenação) e os caminhos de erro do aplicativo.
|
| 361 |
|
| 362 | A cobertura de código é medida, mas não imposta: o relatório orienta o trabalho
|
| 363 | sem jamais reprovar uma \textit{build} ou \textit{deploy}. Persistem, ainda,
|
| 364 | dois riscos de descolamento conhecidos --- os dados de teste escritos à mão em
|
| 365 | cada arquivo e a cópia espelhada dos tipos da API mantida no \textit{frontend}
|
| 366 | podem divergir da fonte de verdade sem que nenhum teste acuse. Essas lacunas
|
| 367 | são assumidas de forma explícita e refletem uma escolha de priorização, e não
|
| 368 | uma garantia de ausência de defeitos. Como a própria definição de teste
|
| 369 | antecipava, uma suíte ``verde'' demonstra apenas que os casos exercitados se
|
| 370 | comportam como esperado.
|