\chapter{Testes}\label{cap:testes} Este capítulo registra como o comportamento do Nexo é verificado. Como nos capítulos anteriores, o compromisso é com a honestidade: não se prescreve uma estratégia ideal, mas se descreve o estado da prática, o que cada tipo de teste é capaz --- e incapaz --- de detectar, e as lacunas assumidas explicitamente na Seção~\ref{sec:test-gaps}. O foco recai sobre a verificação automatizada. A validação com usuários reais não foi realizada neste trabalho: o envolvimento de participantes em uma aplicação de saúde mental exigiria aprovação prévia de comitê de ética em pesquisa, levantamento de pessoas com transtorno de forma a aplicar com elas, cujo todo o trâmite não coube no cronograma do projeto. Essa ausência de avaliação externa é retomada como limitação na Seção~\ref{sec:test-gaps}. Os dados quantitativos citados ao longo do capítulo referem-se ao estado do repositório em meados de outubro de 2026. \section{Vocabulário}\label{sec:test-vocab} Quatro termos delimitam o alcance dos testes descritos adiante: unitário, integração, \textit{job} e \textit{end-to-end} (E2E). Como os nomes e as fronteiras entre esses níveis variam entre autores, o Quadro~\ref{tab:test-vocab} fixa o sentido adotado neste trabalho e o relaciona aos níveis usuais da literatura. \begin{table}[H] \centering \caption{Vocabulário de testes adotado neste trabalho} \label{tab:test-vocab} \small \begin{tabular}{|p{2.8cm}|p{6.6cm}|p{4.2cm}|} \hline \textbf{Termo} & \textbf{O que exercita} & \textbf{Nível usual na literatura} \\ \hline Teste unitário & Uma única função ou módulo, sem HTTP e sem banco de dados. & Teste de unidade. \\ \hline Teste de integração & A aplicação HTTP real (rotas, validação, autenticação, controladores e serviços) sobre um banco de dados real; apenas o que sai do processo é simulado & Teste de integração, em nível de API. \\ \hline Teste de \textit{job} & Um processador de tarefa em segundo plano, chamado diretamente com uma carga fabricada, sobre o banco real. & Teste de integração de componente assíncrono \\ \hline Teste \textit{end-to-end} (E2E) & O aplicativo empacotado, em um dispositivo, ao longo de uma jornada real de usuário. & Teste de sistema. \\ \hline \end{tabular} \source{Elaborado pelo autor (2026)} \end{table} \section{Estratégia de Verificação}\label{sec:test-strategy} O código-fonte do Nexo vive em um único repositório com dois projetos independentes: a API e o aplicativo móvel. O esforço de teste entre eles é deliberadamente assimétrico. A API possui uma suíte automatizada e é protegida por um portão de integração contínua; o aplicativo móvel é verificado majoritariamente de forma manual, com uma suíte \textit{end-to-end} fina por cima. Essa assimetria não é um descuido, e se justifica por duas prioridades. A primeira é a capacidade de trabalho limitada: manter uma suíte automatizada completa nas duas esferas excederia o disponível, e concentra-se o esforço onde o retorno é maior. A segunda é que a API concentra a validação, a autenticação, as regras de negócio e a persistência --- isto é, a garantia sobre a integridade dos dados registrados pelo usuário. A Tabela~\ref{tab:test-tooling} resume as ferramentas envolvidas e o ponto em que cada uma atua. \begin{table}[H] \centering \caption{Ferramentas de verificação} \label{tab:test-tooling} \small \begin{tabular}{|p{4.4cm}|p{4.0cm}|p{5.0cm}|} \hline \textbf{Finalidade} & \textbf{Ferramenta} & \textbf{Onde atua} \\ \hline Execução de testes e asserções da API & Vitest & API \\ \hline Requisições HTTP nos testes & Supertest & Dentro dos arquivos do Vitest \\ \hline Cobertura de código da API & Vitest (\textit{coverage}) & API, sem limiar mínimo \\ \hline Banco de dados de teste & PostgreSQL & \textit{Container} local durante desenvolvimento; serviço efêmero no CI \\ \hline Testes \textit{end-to-end} móveis & Maestro & Dispositivo Android físico, via \textit{adb} \\ \hline \textit{Lint} do aplicativo & ESLint (\texttt{expo lint}) & Manual, fora do CI \\ \hline Integração contínua & GitHub Actions & API \\ \hline \end{tabular} \source{Elaborado pelo autor (2026)} \end{table} \section{Suíte de Testes da API}\label{sec:test-api} A suíte automatizada concentra-se na API e cobre três frentes: os testes unitários, os testes de integração das rotas HTTP e os testes dos processadores de tarefas em segundo plano. No momento da redação, o diretório \texttt{api/tests} reunia 48 arquivos --- 17 unitários, 26 de rotas e cinco de \textit{jobs}. A execução e as asserções ficam a cargo do Vitest; as requisições HTTP são feitas pelo Supertest, que exercita a aplicação Express diretamente, sem abrir uma porta de rede, porém através de sua pilha HTTP real. \subsection{Organização e ferramentas}\label{sec:test-api-org} A configuração do Vitest (\texttt{api/vitest.config.ts}) divide a suíte em dois ``projetos''. O projeto \texttt{unit} seleciona os arquivos com o sufixo \texttt{.unit.test.ts} e não carrega configuração global alguma, de modo que esses testes dependem apenas das funções que exercitam. O projeto \texttt{db} seleciona os demais arquivos --- integração e \textit{job} ---, aplica um arquivo de configuração global, fixa um limite de tempo por teste e faz com que esses testes trunquem e repovoem o banco. A cobertura de código é apenas mensurada, não imposta: o relatório (\texttt{npm run test:coverage}) não possui limiar mínimo nem bloqueia a integração contínua. A verificação estática de tipos (\texttt{npm run typecheck}) também roda em caráter consultivo no CI --- uma falha é registrada, mas não interrompe o \textit{deploy}. \subsection{Execução e banco de dados de teste}\label{sec:test-api-db} Os testes que dependem do banco rodam contra uma instância real do PostgreSQL --- e não contra um substituto em memória ---, em uma base dedicada de nome sufixado com \texttt{\_test}, criada por um pequeno script e materializada com \texttt{prisma db push}. O isolamento entre os casos é obtido por truncamento: antes de cada teste, uma rotina executa \texttt{TRUNCATE users CASCADE}, removendo a tabela de usuários e, por cascata, todas as dependentes. Não há \textit{rollback} transacional nem banco efêmero por execução. O carregamento de dados é feito por funções auxiliares escritas à mão, e não por uma biblioteca de fábricas: um módulo compartilhado (\texttt{api/tests/test-helper.ts}) oferece as primitivas centrais --- cliente Prisma, limpeza da base, criação de usuário e geração de token ---, e cada arquivo define os \textit{seeders} de que necessita. A regra para o uso de dublês na API é: simula-se apenas o que deixa o processo; a aplicação e o banco permanecem reais. A camada de filas (BullMQ) é substituída globalmente por um substituto que registra os \textit{jobs} enfileirados, permitindo afirmar tanto a resposta HTTP quanto o trabalho em segundo plano agendado. O envio de arquivos e o cliente de armazenamento são substituídos por implementações controladas, e o disparo de e-mail e de notificações \textit{push} é simulado pontualmente, nos arquivos que os exercitam. Duas fronteiras permanecem deliberadamente reais: o acesso ao banco, via Prisma, nunca é simulado; tampouco a autenticação. Os testes geram tokens JWT válidos, assinados com o segredo de teste, e os enviam no cabeçalho de autorização, de modo que o \textit{middleware} de autenticação real seja executado. Essa escolha reduz o risco de um teste passar porém se comportar de maneira distinta em produção. \subsection{Tipos de testes}\label{sec:test-api-tipos} A suíte da API divide-se em três grupos, retomando o vocabulário do Quadro~\ref{tab:test-vocab}: unitários, integração e tarefas assíncronas. Cada grupo é descrito a seguir com o que exercita, o que detecta e o que escapa ao seu alcance. \subsubsection{Testes unitários} Os testes unitários exercitam uma única função ou módulo, sem HTTP e sem banco de dados. Concentram-se na lógica pura do sistema, em grande parte extraída deliberadamente de seus envoltórios de persistência para se tornar testável: os agregadores dos indicadores, os construtores dos relatórios e auxiliares compartilhados, como paginação e conversão de agendamento. Também cobrem o gerador de código de ativação, a validade de tokens, os serializadores e as regras de validação. Por serem rápidos e determinísticos, são o instrumento natural para fixar casos-limite --- fronteiras de janelas de tempo, margens de classificação e aritmética de paginação --- e servem como documentação executável dessas regras. A limitação desse nível é estrutural: ele só alcança a lógica isolada em uma costura testável. Sem o banco e a camada HTTP, a maior parte do comportamento permanece fora de alcance, e o nível pouco contribui, isoladamente, para a confiança geral na aplicação. \subsubsection{Testes de integração} Os testes de integração formam a maior parte da suíte. Cada um constrói a aplicação real, conecta um cliente Prisma real e a exercita via Supertest, afirmando tanto a resposta HTTP quanto o estado persistido. Eles percorrem o caminho completo de uma requisição --- roteamento, validação, autenticação, controladores, serviços e persistência --- e, por operarem sobre um PostgreSQL real, não sofrem o descolamento que afeta os substitutos. Afirmar a resposta e as linhas persistidas simultaneamente verifica o efeito da operação, e não apenas o valor de retorno. Em contrapartida, exigem infraestrutura em execução, são mais lentos e, por compartilharem a mesma base, rodam em série. O isolamento por truncamento é suficiente, porém menos rigoroso do que um \textit{rollback} transacional, pois um teste que falha no meio pode deixar resíduos. \subsubsection{Testes de tarefas assíncronas} Os testes de \textit{jobs} importam um processador do BullMQ e o chamam diretamente, com uma carga fabricada, contra o banco real. Cobrem o código que executa fora do ciclo requisição--resposta --- os \textit{workers} que geram exportações, enviam mensagens e recalculam indicadores ---, chegando, no caso da exportação, a descompactar o anexo produzido e inspecionar seu conteúdo. Como o processador é invocado diretamente, a maquinaria da fila em si --- agendamento, repetição, tentativas e ordenação --- permanece fora do alcance. A carga é fabricada à mão e, por isso, pode divergir daquela que o enfileirador realmente envia, sem que nada verifique uma contra a outra. \section{Verificação do Aplicativo Móvel}\label{sec:test-mobile} Em linha com a assimetria descrita na Seção~\ref{sec:test-strategy}, o aplicativo móvel passou a contar com testes unitários em estágio inicial, ainda em andamento e sem a cobertura da suíte da API. A verificação cotidiana permanece predominantemente manual, em um dispositivo físico, contra a pilha local em execução --- o aplicativo roda como \textit{build} de desenvolvimento do Expo, com dependências nativas do Android. Essa verificação captura o que os testes automatizados raramente alcançam --- problemas visuais, de leiaute e de desempenho no dispositivo ---, mas não é repetível, não protege contra regressões e permanece invisível à integração contínua. \subsection{Suíte \textit{end-to-end} (Maestro)}\label{sec:test-mobile-e2e} Sobre a verificação manual há uma suíte \textit{end-to-end} opcional, baseada no Maestro, ferramenta declarativa de automação de interface para dispositivos móveis. A suíte cobre uma única jornada central --- login, registro de humor e saída --- por três fluxos em YAML, marcados como \texttt{smoke} e executados por um pequeno \textit{harness} (\texttt{frontend/scripts/e2e.sh}), exposto como \texttt{npm run test:e2e}. Os fluxos são dirigidos por identificadores de teste (\texttt{testID}) inseridos no código, e não pelo texto visível, o que os torna mais robustos a mudanças de interface. A execução depende de um dispositivo Android físico conectado por \texttt{adb}, de um \textit{build} de desenvolvimento, da API em funcionamento com um usuário de teste semeado e do Metro servindo o aplicativo; não há caminho sem cabeça nem integração ao CI, de modo que a suíte é executada manualmente e com pouca frequência. Cada fluxo limpa o estado do aplicativo e recarrega o \textit{bundle}, tornando a execução lenta, e a cobertura permanece fina: um único caminho feliz, sem casos de erro. A suíte não está integralmente verde no momento da redação: os fluxos de login e de saída passam, mas o de registro de humor falha ao adicionar um componente de humor, porque a tela correspondente é abandonada pelo aplicativo pouco depois de ser aberta --- causa ainda não identificada. \section{Integração Contínua}\label{sec:test-ci} A integração contínua do Nexo \cite{Humble_Farley_2011} é executada no GitHub Actions e testa somente a API, coerente com a estratégia assimétrica. O fluxo de implantação (\texttt{.github/workflows/deploy.yml}) dispara apenas quando há mudanças em \texttt{api}: sobe um PostgreSQL efêmero, instala as dependências, prepara a base de teste, executa a verificação de tipos em caráter consultivo e roda a suíte com o Vitest. A construção da imagem e a implantação em produção são condicionadas ao sucesso dessa etapa, de modo que uma suíte reprovada impede a publicação, conforme o Capítulo~\ref{sec:deploy-and-ci}. O aplicativo móvel permanece fora da integração contínua: \textit{lint} (\texttt{expo lint}) e verificação de tipos (\texttt{tsc --noEmit}) são executados apenas manualmente, e a suíte \textit{end-to-end} não é disparada por fluxo algum. \section{Cobertura e Lacunas}\label{sec:test-gaps} Em síntese, a verificação automatizada cobre a superfície HTTP e a persistência da API, a sua lógica pura e os seus trabalhadores em segundo plano; a verificação do aplicativo móvel restringe-se, por ora, a uma jornada manual de fumaça e a testes unitários ainda em consolidação. Permanecem pouco cobertas a lógica do \textit{frontend} entre o nível de unidade e o \textit{end-to-end}, a maquinaria da fila em si (agendamento, repetições e ordenação) e os caminhos de erro do aplicativo. Persistem, ainda, três limitações que merecem registro. A primeira é a ausência de validação com usuários reais, motivada pelo trâmite de comitê de ética em pesquisa descrito na abertura deste capítulo: as especificações automatizadas atestam o comportamento programado, mas não a adequação do sistema às expectativas do público-alvo. A segunda é a cobertura de código medida, mas não imposta, o que orienta o trabalho sem jamais reprovar uma \textit{build} ou \textit{deploy}. A terceira são os riscos de descolamento já mencionados: os dados de teste escritos à mão em cada arquivo e a cópia espelhada dos tipos da API mantida no \textit{frontend} podem divergir da fonte de verdade sem que nenhum teste acuse. Essas lacunas são assumidas de forma explícita e refletem uma escolha de priorização, não uma garantia de ausência de defeitos, e que podem ser corrigidas em incrementos no futuro.