Testes - ThiagoGomesRocha/Marrone GitHub Wiki
Testes automatizados
Como testar o Marrone em cada camada, e como escrever os test() que rodam dentro de uma
coleção. Para a referência completa de script (todas as funções de bru.*, req.*,
res.*) use a documentação do Bruno — a API é a mesma.
Onde cada teste roda
| Camada | O que cobre | Onde fica | Como rodar |
|---|---|---|---|
| Componente / unidade | funções e componentes React isolados | packages/*/src/**/*.spec.js(x) |
npm test --workspace=packages/marrone-app |
| Runner e sandbox | scripting, bru.*, req.*, res.*, QuickJS |
packages/marrone-js, packages/marrone-* |
npm test --workspace=packages/marrone-js |
| Integração com banco | os cinco drivers contra servidor real | packages/marrone-database/tests/integration.spec.ts |
opt-in, ver abaixo |
| Fim a fim (E2E) | a aplicação Electron real, pela interface | tests/** |
npm run test:e2e |
| Desempenho | tempo do runner | tests/benchmarks |
npm run test:benchmark |
Não existe um npm test na raiz: os testes unitários são por pacote (--workspace).
1. Teste de componente (Jest + jsdom)
Testar um componente é a camada mais barata. O exemplo abaixo é o padrão usado nos specs da
aplicação (packages/marrone-app/src/components/CodeEditor/StatusBar/index.spec.js).
import '@testing-library/jest-dom';
import React from 'react';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import StatusBar from './index';
// O StyledWrapper lê o tema. Sem ThemeProvider o render quebra — não é o teste
// que está errado, é o styled-component que não tem tema nenhum para ler.
const theme = {
colors: { text: { muted: '#888' } },
background: { crust: '#f8f8f8' },
border: { radius: { sm: '4px' }, border0: '#ddd' },
textLink: '#546de5'
};
const renderWithTheme = (ui) => render(<ThemeProvider theme={theme}>{ui}</ThemeProvider>);
describe('StatusBar', () => {
it('mostra o tamanho e o modo selecionado', async () => {
renderWithTheme(<StatusBar />);
expect(screen.getByText('1.2 KB')).toBeInTheDocument();
});
});
Rodar só o seu arquivo:
npm test --workspace=packages/marrone-app -- StatusBar
Duas armadilhas que já custaram tempo aqui:
useTranslationsem provider inicializado. Ojest.setup.jsda app não inicializa o i18next, entãot('COMMON.DOCUMENTATION')devolve a própria chave. Se você consultar o elemento pelo nome acessível, o nome seráCOMMON.DOCUMENTATION, nãoDocumentation. Nesses casos prefira odata-testidque o componente já expõe.- Imports absolutos. O
jest.config.jsda app declaramoduleDirectories: ['node_modules', 'src']e mapeia aliases (components/,utils/,providers/…). Use o mesmo estilo de import do código de produção; caminhos relativos longos (../../../../utils/...) não são o padrão daqui.
2. Teste puro no @marrone/database (sem banco)
O que é função pura se testa sem banco nenhum. É assim que o formato de retorno é travado
(packages/marrone-database/tests/formatResult.spec.ts):
import { formatResult } from '../src/formatResult';
describe('formatResult', () => {
test('devolve um objeto (não um array) quando há um único registro', () => {
const result = formatResult([{ id: 1, nome: 'João' }]);
expect(Array.isArray(result)).toBe(false);
expect(result).toEqual({ id: 1, nome: 'João' });
});
test('devolve null para lista vazia, null e undefined', () => {
expect(formatResult([])).toBeNull();
expect(formatResult(null)).toBeNull();
});
});
npm test --workspace=packages/marrone-database
O npm test desse pacote não abre conexão nenhuma — ele roda em qualquer máquina, sem
Docker. Isso é deliberado: o teste que depende de servidor é outro, abaixo.
3. Integração com banco real (opt-in)
Só roda quando DB_INTEGRATION=true e as variáveis de conexão existem. Sem elas, os
casos ficam skipped e o npm test continua verde.
cd packages/marrone-database
POSTGRES_URL=postgres://localhost:5432/mydb \
POSTGRES_USER=usuario \
POSTGRES_PASSWORD=senha \
DB_INTEGRATION=true npm run test:integration
Variáveis aceitas, uma por SGBD: POSTGRES_URL, MYSQL_URL, MSSQL_URL, ORACLE_URL,
DB2_URL — cada uma com _USER e _PASSWORD.
4. E2E com Playwright (a aplicação de verdade)
Os testes de tests/ sobem o Electron real e clicam na interface. Eles usam os fixtures
de playwright/index.ts:
| Fixture | Para que serve |
|---|---|
page |
a janela da aplicação, já pronta |
createTmpDir(tag?) |
cria um diretório temporário para a coleção do teste |
launchElectronApp(options?) |
sobe o app com um userDataPath isolado (e .env opcional) |
collectionFixturePath / workspaceFixturePath |
copia uma coleção/workspace de fixture para um diretório temporário |
Exemplo — verificando os links da tela de suporte (tests/preferences/support-links.spec.js):
import { test, expect } from '../../playwright';
test('Should verify all support links with correct URL in preference > Support tab', async ({ page }) => {
await page.getByLabel('Open Preferences').click();
await page.getByRole('tab', { name: 'Support' }).click();
const documentation = page.getByRole('link', { name: 'Documentation', exact: true });
expect(await documentation.getAttribute('href')).toEqual(
'https://github.com/ThiagoGomesRocha/Marrone/wiki'
);
});
E um teste que cria coleção em diretório temporário:
test('cria coleção em um diretório temporário', async ({ page, createTmpDir }) => {
const testDir = await createTmpDir('test-collection');
await page.getByLabel('Create Collection').click();
await page.getByLabel('Name').fill('minha-colecao');
await page.getByLabel('Location').fill(testDir);
await expect(page.getByText('minha-colecao')).toBeVisible();
});
npm run test:e2e # projetos default + system-pac
npm run test:e2e:sanity # só o subconjunto marcado com @sanity (rápido, para o dia a dia)
npm run test:e2e:auth # projeto auth
npm run test:e2e:ssl # projeto ssl
npm run test:e2e:mock-server
npm run test:codegen minha-nova-spec # grava seus cliques e gera o teste
Dois detalhes que economizam tempo:
- Rode
@sanitydurante o desenvolvimento. A suíte E2E inteira é lenta (sobe Electron a cada teste) e não é o laço de feedback certo para editar um componente. - Teste que depende da rede é frágil.
tests/import/url-import/*baixa uma fixture de uma URL real; se ela mudar de lugar, o teste passa a falhar por motivo alheio ao código. Existe caso que estende otest.setTimeoutjustamente porque o fetch pode demorar.
5. test() dentro da coleção (o que você escreve)
Toda requisição tem um bloco Tests, executado depois da resposta. A asserção é no estilo
chai (.to.equal, .to.contain, .to.eql) e o callback pode ser async:
test('deve retornar 200 e ecoar o parâmetro', function () {
expect(res.getStatus()).to.equal(200);
expect(res.getBody().args.param).to.equal('valor');
});
test('grava o valor na variável de ambiente', async function () {
await bru.setEnvVar('token', res.getBody().token);
expect(bru.getEnvVar('token')).to.not.be.undefined;
});
O await só existe dentro de função async — por isso os exemplos de banco e de HTTP usam
async function (). Além disso: um test() que lança não derruba a requisição seguinte; o
resultado de cada asserção aparece na aba Tests e no relatório de execução.
Para a lista completa de test, expect, bru, req e res (incluindo bru.sendRequest,
bru.runRequest, bru.runner.setNextRequest, cookies e bru.utils), use a
documentação do Bruno — a superfície de script é a mesma.
Para validar dados no banco dentro de um test(), veja
Scripts de banco de dados.
Boas práticas
- Teste o comportamento, não a implementação. Nomeie o teste pelo que o usuário observa ("mostra o tamanho selecionado") e não pelo método que você chamou.
- Um motivo de falha por teste. Um
expectpor comportamento deixa claro o que quebrou sem precisar ler o stack. - E2E sempre em diretório temporário. Use
createTmpDir— nunca aponte um teste para uma coleção que existe na sua máquina. - Nunca coloque credencial em coleção, fixture ou
.bru. Veja o princípio em Scripts de banco de dados. - Travou um defeito? Deixe o teste que reproduz. Um teste que falha antes do conserto e passa depois vale mais que a descrição do bug na issue.
- Cuidado com teste que compara com o próprio componente. Se o teste importa a URL do componente e compara com o componente, ele passa mesmo quando a URL está errada — foi o que aconteceu com os links de suporte. Nesses casos, escreva o valor esperado literal no teste.