Popular banco de dados MySQL para testes de integração.
Tags: docker, node, javascript, testing
Published: 2020-05-03 01:44:47

Hey,

Escrevo esse artigo para ajudar os outros devs que passaram por uma situação parecida com a que tive no projeto que estou atuando. Dockerizamos todos os nossos testes tanto os de unidade quanto os de integração (Sistêmicos), porém para realizarmos os testes de integração era necessário criarmos e popularmos o banco de dados, pesquisamos em vários fóruns e de todas as formas possiveis e nada foi encontrado, então resolvi fazer uma POC que solucionou nossos problemas e nela foi utilizado:
- [NodeJS](https://nodejs.org/en/); 
- Package do [MySQL](https://www.npmjs.com/package/mysql); 
- [Docker](https://www.docker.com/).

Antes de começarmos uns pontos a se atentar:
- Tinha um arquivo com todas as Queries SQL para criação do banco e das tabelas, um inserindo dados mockados e outro para derrubar todas as tabelas.

## Script em Javascript usando o Package MySQL
```
const mysql = require('mysql');
const fs = require('fs');

// Aqui usei uma IIFE e uma função anonima, para nenhum metodo desse arquivo seja usado fora dele
(async function() {
  try {
    // Aqui realizamos a conexão com o banco de dados MySQL
    const mysqlConn = await mysql.createConnection({
      host: process.env.MYSQL_HOSTNAME,
      port: process.env.MYSQL_PORT,
      user: process.env.MYSQL_USER,
      password: process.env.MYSQL_PASSWORD,
      database: process.env.MYSQL_DATABASE,
      // Como os arquivos com as queries tinham várias declarações eu settei para true o mesmo 
      multipleStatements: true,
    });

    // Aqui connecto com mysql
    await mysqlConn.connect();

    // Esse primeiro método ele cria o banco de dados caso não exista e cria as tabelas do banco
    const createTablesNDB = async () => {
      try {
        const tablesNDB = fs.readFileSync(`${__dirname}/database.sql`, 'utf8');
        await mysqlConn.query({ sql: tablesNDB.toString() });
        // Aqui settei um timeout por algum motivo o script estava pulando para o process.exit()
        setTimeout(() => {
          process.exit();
        }, 1500);
      } catch (err) {
        console.log(err);
      }
    };

    // Nesse método é inserido os dados mockados nas tabelas criadas
    const inserirDados = async () => {
      try {
        const data = fs.readFileSync(`${__dirname}/_data.sql`, 'utf8');
        await mysqlConn.query({ sql: data.toString() });
        setTimeout(() => {
          process.exit();
        }, 1500);
      } catch (err) {
        console.log(err);
      }
    };
    
    // Nesse outro método é dropado todas as tabelas para não haver conflitos de FK, ao rodar o banco novamente e toda vez que forem executados os testes recriar o banco e as tabelas, para não viciarmos nossos testes
    const removerTabelas = async () => {
      try {
        const dropTables = fs.readFileSync(
          `${__dirname}/_drop.sql`,
          'utf8',
        );
        await mysqlConn.query({ sql: dropTables.toString() });
        setTimeout(() => {
          process.exit();
        }, 1500);
      } catch (err) {
        console.log(err);
      }
    };

    // Aqui criamos uma CLI simples onde possui 3 subcomandos
    // O process.argv ele é um array com os argumentos passados em um comando que no caso nosso comando seria $ node db.js --[create || insert || drop]
    if (process.argv[2] === '--create') {
      criarTabelaseBD();
    } else if (process.argv[2] === '--insert') {
      inserirDados();
    } else if (process.argv[2] === '--drop') {
      removerTabelas();
    }
  } catch (err) {
    // Aqui logamos todos os erros
    console.error('ERROR >>>>>>>>>>>', err, 'MESSAGE >>>>>', err.message);
  }
})();
```
Com esse arquivo você pode criar um arquivo `.sh` e passar a ordem dos comandos, exemplo:
```
#!/bin/bash

npm test &&
node ./db.js --drop &&
node ./db.js --create &&
node ./db.js --insert &&
npm run test:integration && 
node ./db.js --drop

```
No seu Dockerfile de testes na linha de `CMD` você pode rodar `["sh", "./tests.sh"]` que o Docker fará toda mágica para você hehe, não esqueçam de settar o `env-file` caso estiver usando `docker-compose` e também de settar as variáveis de ambiente no seu arquivo de environment. 


E é isso pessoal, adendo esse código é uma proof of concepts.

Qualquer dúvida é só deixar nos comentários que eu responderei. 

LinkedIn: https://www.linkedin.com/in/matheusviana/
GitHub: https://github.com/moviandev

Até a próxima meus camaradas.