eletrotupi / tcc/ monografia/chapters/tests.texe3c26408e072503a207669ead4634c72f5b079f9
17.7 KB | 370 lines Raw
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.