Introdução ao Teste de Scripts
Agora que validamos que nosso script está funcionando conforme o esperado e gerou a quantidade de linhas com registros que solicitamos, vamos escrever o teste dele usando o testRunner nativo do Node.
Um script não é diferente de rotas ou funções. Na verdade, ele é uma função que executa o que colocamos ali dentro, e precisamos validar se isso realmente está de acordo com o que esperamos. Precisamos verificar se ele está gerando um livro válido, se o formato do arquivo está correto e se a quantidade de linhas é exatamente a que passamos. Assim como fazemos um teste unitário de uma função, devemos fazer o teste unitário também para nossos scripts, para garantir que eles funcionem da maneira esperada.
Configuração do Ambiente de Teste
Vamos, então, à pasta raiz do projeto. Criaremos uma pasta chamada "testes" e, dentro dela, uma pasta chamada "scripts". Em seguida, criaremos um novo arquivo chamado testgeneratebooks.js. Por enquanto, não utilizaremos a nomenclatura de spec nem a nomenclatura de .teste, mas deixaremos isso para depois.
Primeiro, vamos colar alguns imports que já deixaremos prontos para não perdermos muito tempo digitando. Estamos importando a função test do Node test, do test runner nativo, o assert, o fs do fs/promises, o path e o url para normalizar nossos caminhos. Também importaremos a função do nosso script para gerar os livros.
import { test } from "node:test";
import assert from "node:assert";
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { generateBooksToNDJSON } from "../../scripts/generateBooks.js";
Normalização de Caminhos e Limpeza de Arquivos
Como estamos trabalhando dentro da pasta de teste, não queremos que, ao executar nosso script, o arquivo seja criado na raiz do projeto. O Node cria o arquivo no local de onde você executou. Portanto, se executarmos da raiz, ele criará o arquivo na raiz. Queremos que o arquivo de livros seja criado dentro da pasta "scripts", pois é lá que estamos trabalhando agora, para não poluir nosso ambiente.
Tudo relacionado ao teste deve ser mantido em um local específico. Para isso, vamos realizar uma normalização. Assim, const __dirname recebe path.dirname, passando o fileURLToPath e o import.meta.url.
const __dirname = path.dirname(fileURLToPath(import.meta.url));
Se estivermos acostumados a usar outros test runners, como no curso de testes unitários em que utilizamos o Jest, temos funções como before, beforeAll, afterAll, que executam antes e depois dos testes. No ambiente nativo, ainda não dispomos dessas funções. Portanto, desejamos que, sempre que nosso script de teste for executado, ele limpe o arquivo de livros no início e no final, removendo esses arquivos para garantir que cada teste seja independente.
Como não temos as funções afterAll e beforeAll, vamos criar uma função para limpar o arquivo e chamá-la em cada um dos testes, conforme necessário. Vamos criar uma função assíncrona chamada cleanupFile, que receberá como parâmetro o caminho do arquivo, filePath.
async function cleanupFile(filePath) {
try {
await fs.unlink(filePath);
} catch (error) {
if (error.code !== 'ENOENT') throw error;
}
}
Criação e Execução do Teste
Agora, vamos criar um próximo teste, semelhante ao que vimos no Jest. Podemos definir um nome para o teste e o que ele executará. Neste caso, o teste deve gerar o número correto de livros. Passaremos uma função síncrona como callback, que será executada no teste.
test('Deve gerar o número correto de livros', async () => {
const testFilePath = path.join(__dirname, "test-books.ndjson");
const numberOfBooks = 100;
await cleanupFile(testFilePath);
await generateBooksToNDJSON(testFilePath, numberOfBooks);
const content = await fs.readFile(testFilePath, "utf-8");
const lines = content.trim().split("\n");
assert.strictEqual(lines.length, numberOfBooks, `Deveria gerar ${numberOfBooks} livros`);
});
Dentro do teste, definiremos const testFilePath como path.join, passando __dirname e test-books.ndjson, que será o nome do arquivo de livros utilizado. Assim, ele será gerado dentro da pasta "script" e se chamará test-books.ndjson. Também definiremos const numberOfBooks para especificar o número de livros, facilitando alterações futuras.
Iniciaremos chamando cleanupFile para garantir que o arquivo test-books não exista antes de criá-lo. Em seguida, chamaremos await generateBooksToNDJSON, passando o caminho do arquivo de teste e o número de livros a serem inseridos.
Após isso, contaremos o número de linhas. Utilizaremos const content = await fs.readFile, lendo o arquivo test-books.ndjson em utf8. Em seguida, criaremos const lines, que receberá o conteúdo da leitura, removendo espaçamentos e dividindo a string por \n. Isso nos permitirá contar quantas linhas existem no arquivo e verificar se o número de livros está correto.
Faremos uma asserção com assert.strictEqual, passando o valor atual lines.length, o valor esperado numberOfBooks e uma mensagem opcional para facilitar a identificação de erros no terminal. A mensagem será "Deveria gerar" seguido do número de livros.
Execução do Teste e Considerações Finais
No final, como teremos outros testes, deletaremos o arquivo. No entanto, na primeira execução, deixaremos essa linha comentada para verificar se o arquivo foi gerado corretamente.
Para executar, abriremos o terminal, fecharemos o explorador de arquivos e executaremos node testes-scripts-testGenerateBooks.js. Ao dar enter, o teste falhou, indicando que não encontrou o arquivo, apesar de ter gerado um arquivo com 100 registros. Isso ocorreu devido a uma race condition, que resolveremos no próximo vídeo.