Seus testes de API são aprovados no seu laptop. A verdadeira questão é se eles são executados em cada pull request, cada merge, cada build noturno, sem que um humano clique em nada. Essa tarefa pertence a um executor de linha de comando. Ele pega os testes que você já criou e os executa de forma *headless* dentro do seu *pipeline*, sai com um código de status limpo e escreve um relatório que seu painel de CI pode ler.
Dois *runners* (executores) surgem constantemente quando as equipes configuram isso: o Bruno CLI e o Apidog CLI. Eles resolvem o mesmo problema a partir de pontos de partida diferentes. Bruno é um cliente de API *git-native*, *offline-first* e de código aberto, e seu CLI executa os arquivos .bru que residem no seu repositório. Apidog é uma plataforma de API completa, e seu CLI executa os cenários de teste visuais que você constrói no aplicativo. Ambos se integram ao GitHub Actions, GitLab CI, Jenkins e qualquer outra coisa com Node.js. Ambos falham a *build* quando um teste falha. As diferenças aparecem em como você cria os testes, como você se autentica e como a definição do teste viaja para o CI.
Esta é uma comparação honesta, em nível de comando, dos dois. Sem *strawmen* (argumentos de espantalho). Bruno faz várias coisas genuinamente bem, e você verá exatamente onde cada *runner* se encaixa antes de conectá-lo ao seu *pipeline*.
EM RESUMO
- Bruno CLI (
@usebruno/cli, bináriobru) executa arquivos.brudiretamente de uma pasta no seu repositório git. É de código aberto, *offline* e não precisa de conta ou token. Você o aponta para um diretório e ele executa cada requisição e asserção que encontra. - Apidog CLI (
apidog-cli, binárioapidog) executa os cenários de teste que você projeta no aplicativo Apidog, buscados por ID usando um *access token*. Você não reescreve testes como código; o cenário visual é o teste, e o CLI o executa de forma *headless*. - Ambos emitem relatórios JUnit, JSON e HTML, e ambos falham a *build* em caso de teste falho. O JUnit XML é o que se conecta aos painéis de CI em ambos os casos.
- Escolha **Bruno** quando quiser tudo no repositório, zero contas e controle *offline* total de arquivos de teste em texto simples.
- Escolha **Apidog** quando quiser autoria visual, encadeamento de cenários, gerenciamento de ambientes e execuções *data-driven* sem manter código de teste manualmente.
O problema real: testes que existem mas nunca são executados
Um teste que você executa manualmente é um teste que apodrece. Alguém o construiu, ele passou uma vez, e então permaneceu intocado enquanto a API mudava por baixo dele. A solução não é mais testes. São testes que são executados automaticamente a cada mudança, com um sinal de passa/falha que o *pipeline* pode usar.
Um *runner* CLI é o que fecha essa lacuna. Ele precisa de três coisas para ser útil no CI: ele deve ser executado sem uma GUI, deve sair com um código diferente de zero quando algo falha para que a *build* fique vermelha, e deve escrever um relatório legível por máquina para que os revisores vejam o que quebrou. Bruno e Apidog ambos atingem esse nível. Onde eles diferem é antes do comando de execução, em como o teste foi escrito e onde ele reside.
Se você está configurando o CI do zero, os padrões mais amplos em automatização de testes de API em CI/CD valem a pena ser lidos junto com esta comparação. Aqui nos concentramos nos dois *runners* em si.
O que o Bruno CLI faz bem
Todo o design do Bruno é *git-native*. Cada requisição, ambiente e asserção é um arquivo .bru em texto simples no disco, dentro do seu repositório, versionado como qualquer outro arquivo-fonte. Esse modelo tem vantagens reais, e vale a pena declará-las claramente antes de qualquer comparação.
Seus testes vivem com seu código. Um *pull request* que altera um *endpoint* pode mudar o teste para aquele *endpoint* no mesmo *diff*, revisado pela mesma pessoa. Não há um sistema separado para sincronizar, nenhuma cópia na nuvem que possa se desviar do que está no repositório. Os *diffs* são legíveis porque o formato é texto. Você pode *grep* seus testes, refatorá-los com as mesmas ferramentas que usa para código e resolver conflitos de *merge* em um editor.
Também é de código aberto e *offline*. O CLI é executado inteiramente na sua máquina ou no seu *runner* de CI sem conta, login e token. Para equipes com regras rígidas de manuseio de dados ou ambientes isolados (*air-gapped*), isso importa. O nível pago do Bruno, Bruno Ultimate, adiciona recursos de equipe, incluindo SSO e SCIM, integrações com gerenciadores de segredos e recursos de auditoria, então o projeto não é apenas uma ferramenta para *hobbystas*. Mas o cliente principal e o CLI são gratuitos e autônomos, e essa é uma força legítima.
A instalação é um único comando:
npm install -g @usebruno/cli
O binário é bru. Você executa uma coleção apontando-o para a pasta que contém seus arquivos .bru:
bru run --env staging
Execute de dentro do diretório da coleção e bru run executa as requisições que encontra lá. Adicione -r para percorrer subpastas e assim ele encontra requisições aninhadas:
bru run -r --env staging
Esse é o *loop* principal. Sem IDs, sem token, sem *remote fetch*. Os arquivos na pasta são os testes, e o CLI os executa.
Abordamos a história mais ampla do Bruno em o que torna Bruno diferente como cliente de API git-native e onde suas limitações aparecem para equipes maiores. Para o CI especificamente, as forças acima são as que contam.
O que o Apidog CLI faz bem
Apidog segue um caminho diferente para o mesmo *pipeline*. Você constrói testes visualmente no aplicativo Apidog: encadeia requisições em um cenário, adiciona asserções, extrai um valor de uma resposta para a próxima requisição e repete todo o processo sobre um arquivo de dados. O CLI é o executor *headless* para esses cenários. Ele não tem seu próprio formato de arquivo. Ele busca o cenário que você nomeia por ID do seu projeto Apidog e o executa exatamente como o aplicativo faria.
A vantagem é que ninguém mantém duas representações do mesmo teste. O cenário no projeto é o teste. Você o cria em um construtor visual que lida com encadeamento de requisições, extração de variáveis e asserções sem que você precise escrever e depurar arquivos de *script*. Então o CLI executa esse mesmo cenário no CI. O *loop* de autoria rápida e o *loop* de automação usam uma única fonte de verdade.
A instalação é um único comando npm:
npm install -g apidog-cli
O binário é apidog. Uma execução típica nomeia um cenário por ID, escolhe um ambiente e se autentica com um *access token*:
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 -e 1629989 -n 1 -r html,junit
Você não digita esses IDs manualmente. Abra o cenário de teste, mude para sua aba CI/CD, gere um *access token*, e o Apidog constrói o comando completo para você com o ID do cenário e o ID do ambiente já preenchidos. Você o copia uma vez, então move o token para um segredo de CI e o referencia como $APIDOG_ACCESS_TOKEN. A referência completa de *flags* está no guia completo do Apidog CLI se você quiser todas as opções em um só lugar.
O modelo baseado em token é a diferença mais clara em relação ao Bruno. O Apidog executa testes armazenados em um projeto que o CLI acessa pela rede, autenticado. O Bruno executa testes armazenados como arquivos que o CLI lê do disco. Nenhum está errado; eles se adequam a diferentes configurações de equipe.
Lado a lado
| Dimensão | Bruno CLI (bru) |
Apidog CLI (apidog) |
|---|---|---|
| Pacote | @usebruno/cli |
apidog-cli |
| Comando de execução | bru run |
apidog run |
| Fonte do teste | Arquivos .bru no seu repositório git |
Cenários de teste no seu projeto Apidog, buscados por ID |
| Autoria | Edição manual de arquivos de texto ou uso do aplicativo Bruno | Construtor de cenário visual no aplicativo Apidog |
| Autenticação no CI | Nenhuma; executado offline | Access token (--access-token) |
| Selecionar o que será executado | Caminho da pasta, -r recursivo, --tags |
-t cenário, -f pasta, --test-suite |
| Ambiente | --env <name> |
-e <environmentId> |
| Orientado a dados | --csv-file-path, --json-file-path |
-d <path> (CSV ou JSON) |
| Iterações | --iteration-count <n> |
-n <n> |
| Geradores de relatórios | JSON, JUnit, HTML | cli, html, json, junit |
| Falha rápida | --bail |
--on-error end (o padrão falha no primeiro erro) |
| Código aberto | Sim | Não (CLI npm gratuito; executa cenários do seu plano) |
| Licença/conta | Nenhuma para o CLI | Conta Apidog para o projeto |
Duas coisas se destacam. Primeiro, ambos os *runners* cobrem os mesmos essenciais do CI: seleção de ambiente, iteração orientada a dados, os três formatos de relatório importantes e uma saída diferente de zero em caso de falha. Segundo, a divisão é sobre onde o teste reside e como você o escreveu, não sobre capacidade bruta. Bruno mantém o teste no repositório como texto. Apidog o mantém no projeto como um cenário visual e o executa por referência.
Geradores de relatórios e códigos de saída: as partes que o CI realmente lê
Um *runner* ganha seu lugar em um *pipeline* por meio de dois comportamentos: o relatório que ele escreve e o código de saída que ele retorna. Acertando isso, o resto é fiação.
Bruno escreve relatórios com *flags* por formato. Você passa um caminho para cada formato desejado:
bru run -r --env staging \
--reporter-junit ./results/junit.xml \
--reporter-html ./results/report.html \
--reporter-json ./results/report.json
O JUnit XML é o que seu painel de CI analisa em uma árvore de aprovação/reprovação. O relatório HTML é um artefato navegável. O --bail do Bruno interrompe a execução após a primeira requisição, teste ou asserção falha, o que mantém o feedback rápido em um teste de fumaça (*smoke test*). Sem --bail, ele executa tudo e relata todas as falhas de uma vez.
Apidog usa uma única *flag* -r com uma lista separada por vírgulas, e escreve tudo em um único diretório de saída:
apidog run --access-token $APIDOG_ACCESS_TOKEN -t 605067 \
-r html,junit --out-dir ./apidog-reports
Sua *flag* --on-error molda o comportamento no meio do cenário: end para na primeira falha (o padrão), continue executa cada passo para que você colete todas as falhas em um relatório, e ignore pula um passo conhecido por ser instável (*flaky*). De qualquer forma, a execução termina com um código diferente de zero se algo falhou.
O contrato do código de saída é o mesmo em ambos os lados e é a parte fundamental. Quando uma asserção falha, o *runner* sai com um código diferente de zero. O CI lê esse código, marca o passo como falho, falha o *job* e bloqueia o *merge* ou o *deploy*. Você não configura nada extra. A única armadilha, idêntica para ambos, é engolir o código de saída: se você envolver a execução em um *shell pipeline* ou adicionar || true, a saída diferente de zero é "comida" e o *gate* para de funcionar silenciosamente. Não faça isso.
Bruno CLI no GitHub Actions
Como os arquivos .bru já estão no repositório, o *workflow* é curto. Faça o *checkout* do código, instale o CLI, execute a coleção, faça o *upload* do relatório.
name: Testes de API
on:
pull_request:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configurar Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Instalar Bruno CLI
run: npm install -g @usebruno/cli
- name: Executar testes de API
working-directory: ./api-tests
run: bru run -r --env staging --reporter-junit ./results/junit.xml
- name: Fazer upload do relatório
if: always()
uses: actions/upload-artifact@v4
with:
name: relatorio-bruno
path: ./api-tests/results
O working-directory aponta para a pasta que contém sua coleção. if: always() mantém o *upload* do relatório em execução mesmo quando os testes falham, que é exatamente quando você quer lê-lo. Nenhum segredo é necessário porque nada se autentica a um serviço remoto.
Apidog CLI no GitHub Actions
O *workflow* do Apidog é estruturalmente o mesmo, com uma adição: o *access token* vem de segredos do repositório, e você seleciona o cenário por ID em vez de por caminho da pasta.
name: Testes de API
on:
pull_request:
branches: [main]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configurar Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Instalar Apidog CLI
run: npm install -g apidog-cli
- name: Executar cenário de teste de API
run: |
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t 605067 \
-e 1629989 \
-r html,junit \
--out-dir ./apidog-reports
env:
APIDOG_ACCESS_TOKEN: ${{ secrets.APIDOG_ACCESS_TOKEN }}
- name: Fazer upload do relatório
if: always()
uses: actions/upload-artifact@v4
with:
name: relatorio-apidog
path: ./apidog-reports
Note a simetria. Mesmo *checkout*, mesma configuração de Node, mesmo formato instalar-e-executar, mesmo *upload* sempre. As únicas diferenças reais são o token configurado como um segredo e o cenário selecionado por ID. Se você quiser isso expandido com variantes para GitLab CI e Jenkins também, o guia completo do Apidog CLI mantém o mesmo padrão entre os *runners*.
Como escolher
A decisão raramente se resume a qual *runner* é "melhor". Ela se resume a como sua equipe deseja criar e armazenar os testes.
Escolha Bruno quando o repositório é a fonte da verdade. Se você quer cada teste como um arquivo de texto simples vivendo ao lado do código que ele cobre, revisado no mesmo *pull request*, sem conta e sem chamada de rede em tempo de execução, Bruno se encaixa exatamente nesse modelo. É a escolha natural para equipes que tratam testes como código, valorizam ferramentas *offline* e de código aberto, e se sentem à vontade para editar arquivos .bru diretamente. Bruno Ultimate adiciona recursos de SSO, SCIM, integrações com gerenciadores de segredos e auditoria se você precisar mais tarde da camada de equipe e governança, então crescer com ele é uma opção em vez de uma barreira.
Escolha Apidog quando a velocidade de autoria e um *workflow* integrado importam mais do que o controle em nível de arquivo. Se você prefere construir um cenário de teste visualmente, encadear requisições, extrair variáveis e executar o mesmo cenário em vários ambientes sem escrever e depurar código de teste manualmente, o modelo do Apidog remove essa fricção. Design, depuração, *mock* e teste vivem em um único espaço de trabalho, e o CLI executa o cenário exato que você construiu. Para equipes vindo de uma configuração Postman, o modelo mental se alinha de forma limpa, e o caminho de migração é um que o Apidog cobre como uma alternativa ao Postman para testes de API.
Há também uma resposta para ambas as ferramentas. Algumas equipes mantêm os arquivos *git-native* do Bruno para verificações de requisição de baixo nível e usam o Apidog para os cenários encadeados maiores e execuções de regressão com muitos ambientes. Os dois CLIs coexistem bem em um *pipeline*; são passos separados com códigos de saída separados.
Se você tem estado a decidir entre as plataformas de forma mais ampla, não apenas os CLIs, a comparação Apidog vs Bruno cobre design, *mocking* e colaboração além da linha de comando. Para configurar seu primeiro cenário automatizado e executá-lo a partir do terminal na mesma tarde, baixe o Apidog e copie o comando gerado da aba CI/CD de qualquer cenário.
Perguntas frequentes
O Bruno CLI é gratuito?
Sim. O Bruno CLI é de código aberto e é distribuído como o pacote npm @usebruno/cli. Ele é executado inteiramente na sua máquina ou *runner* de CI sem conta ou token. Bruno Ultimate é um nível pago separado que adiciona recursos de equipe e governança como SSO, SCIM, integrações com gerenciadores de segredos e auditoria, mas o CLI em si é gratuito.
O Apidog CLI é gratuito?
O CLI é um pacote npm gratuito, apidog-cli. Ele executa os cenários de teste do seu projeto Apidog, então o que você pode executar depende do seu plano Apidog, mas o *runner* de linha de comando não é um produto pago separado.
Preciso escrever testes como código para algum dos runners?
Para o Bruno, seus testes são arquivos .bru que você pode editar manualmente ou criar no aplicativo Bruno, então há um formato de texto que você irá manipular diretamente. Para o Apidog, você constrói cenários visualmente no aplicativo e o CLI os executa por ID, então você não mantém código de teste manualmente. Essa é a principal diferença de autoria entre os dois.
Ambos os runners falham a build quando um teste falha?
Sim. Ambos saem com um código diferente de zero quando uma asserção falha, o que o CI lê para marcar o passo como falho e bloquear o *merge* ou *deploy*. O --bail do Bruno para na primeira falha; o --on-error end do Apidog faz o mesmo e é o padrão. Evite envolver qualquer uma das execuções em || true, o que engole o código de saída e quebra o *gate*.
Qual formato de relatório devo usar no CI?
Use JUnit XML para o resultado legível por máquina que seu painel de CI analisa em uma árvore de aprovação/reprovação, e adicione HTML se você quiser um artefato navegável. Bruno os escreve com --reporter-junit e --reporter-html; Apidog usa -r html,junit. Ambos também suportam JSON para pós-processamento personalizado.
O Bruno CLI precisa de conexão com a internet ou conta para ser executado?
Não. Bruno executa os arquivos .bru do seu repositório localmente, sem login e sem *remote fetch*, o que o torna adequado para CI *offline* ou *air-gapped*. O CLI do Apidog se autentica com um *access token* e busca o cenário do seu projeto, então ele precisa de acesso à rede para o serviço Apidog em tempo de execução.
Posso executar qualquer um dos CLIs sem uma instalação global?
Sim para ambos. Use npx @usebruno/cli run ... ou npx apidog-cli run ... para executar sem uma instalação global persistente, o que é conveniente em *runners* de CI efêmeros. Execute bru run --help ou apidog run --help para confirmar as opções exatas disponíveis na sua versão instalada.
