Simulação de comandos e criação de substitutos para ferramentas externas
Substitua o PATH e defina binários falsos para testar scripts sem tocar nos sistemas reais.
Simulação de comandos e criação de substitutos para ferramentas externas é uma aula grátis de Linux Command Line & Bash Scripting Mastery no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
Por que simular comandos nos testes Bash?
Ao testar um script Bash que chama curl, aws, git ou qualquer ferramenta externa, você enfrenta um problema: as chamadas reais acessam a rede, modificam o estado, custam dinheiro ou simplesmente falham em um ambiente de integração contínua no qual essas ferramentas não estão instaladas.
Simular significa substituir o comando real por um falso que você controla. Seu falso (o stub) retorna uma saída e códigos de saída previsíveis, fazendo com que o teste seja rápido, isolado e reproduzível.
- Nenhum acesso à rede ou à nuvem é necessário
- Os testes são executados em milissegundos, em vez de segundos
- Você pode simular erros difíceis de provocar em sistemas reais
- Os pipelines de integração contínua permanecem limpos e sem dependências
O Bash oferece um mecanismo surpreendentemente simples para isso: basta colocar o seu binário falso em algum local anterior ao binário real no $PATH.
Como funciona a busca no PATH
Quando o shell executa um comando como curl, ele pesquisa cada diretório em $PATH, da esquerda para a direita, e executa a primeira correspondência encontrada.
Isso significa que, se você colocar no início um diretório que contenha seu próprio script curl, o shell nunca chegará a /usr/bin/curl.
O padrão de substituição:
- Crie um diretório temporário (seu diretório de binários simulados)
- Grave um executável falso com o mesmo nome do comando real
- Coloque esse diretório no início de
PATH - Execute o script em teste — ele chamará sua simulação, não o binário real
- Limpe o diretório temporário depois do teste
Isso funciona sem acesso de administrador, sem modificar arquivos do sistema e sem nenhum framework especial.
Criando um diretório de simulações
O padrão convencional usa mktemp -d para criar um diretório temporário isolado para suas simulações. Cada teste ou conjunto de testes recebe seu próprio diretório, evitando a contaminação entre testes.
Depois que o teste for concluído, remova o diretório com rm -rf. Usar um trap garante a limpeza mesmo quando o teste termina antecipadamente devido a um erro.
#!/usr/bin/env bash
# Setup a stub bin directory for testing
# Create the temp dir
STUB_BIN=$(mktemp -d)
# Always clean up on exit (success, error, or signal)
trap 'rm -rf "$STUB_BIN"' EXIT
# Prepend it to PATH so our stubs take priority
export PATH="$STUB_BIN:$PATH"
echo "Stub bin: $STUB_BIN"
echo "PATH starts with: ${PATH%%:*}"
# Your tests would go here...
echo "Tests complete."Escrevendo sua primeira simulação
Uma simulação é simplesmente um arquivo executável com o mesmo nome do comando que você deseja substituir. Ela imprime a saída esperada pelo script em teste e termina com o código que você escolher.
Regras principais para simulações:
- O arquivo deve ser executável (
chmod +x) - A linha de shebang (
#!/usr/bin/env bash) é obrigatória - Mostre a saída que seu script real analisaria
- Use
exit 0para indicar sucesso e um valor diferente de zero para falhas simuladas
#!/usr/bin/env bash
# Create a stub for 'curl' that returns a fake HTTP response
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"
# Write the stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
# Fake curl: always returns a 200 OK with a JSON body
echo '{"status": "ok", "version": "1.2.3"}'
exit 0
EOF
chmod +x "$STUB_BIN/curl"
# Verify the stub is found before the real curl
which curl
curl https://example.com/api/versionTestando um script que chama curl
Agora vamos juntar tudo. Suponha que você tenha um script de implantação que chama curl para verificar um endpoint de integridade e termina com um erro se o serviço não estiver saudável. Você quer testar tanto o caminho de sucesso quanto o caminho de falha sem usar um servidor real.
#!/usr/bin/env bash
# Script under test: check_health.sh
# It calls curl and checks the returned JSON
check_health() {
local url="$1"
local response
response=$(curl -sf "$url")
if [[ "$response" == *'"healthy":true'* ]]; then
echo "Service is UP"
return 0
else
echo "Service is DOWN" >&2
return 1
fi
}
# ---- Test harness ----
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"
# Happy path stub
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
chmod +x "$STUB_BIN/curl"
check_health "http://fake-host/health" && echo "PASS: healthy response"Simulação de Falhas de Comandos
Um dos usos mais valiosos dos stubs é simular falhas difíceis de reproduzir com ferramentas reais — tempos limite de rede, erros de permissão, disco cheio ou uma API remota que retorna um 500.
Para simular uma falha, basta fazer o stub sair com um código diferente de zero. Também pode escrever em stderr exatamente como o comando real faria, para que o tratamento de erros do seu script seja exercitado por completo.
#!/usr/bin/env bash
# Test that check_health handles a curl failure gracefully
check_health() {
local url="$1"
local response
# -f makes curl exit non-zero on HTTP error; -s silences progress
if ! response=$(curl -sf "$url" 2>/dev/null); then
echo "ERROR: could not reach $url" >&2
return 1
fi
echo "OK: $response"
}
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"
# Failure stub — simulates a network error (curl exit code 6 = could not resolve host)
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo 'curl: (6) Could not resolve host: fake-host' >&2
exit 6
EOF
chmod +x "$STUB_BIN/curl"
if ! check_health "http://fake-host/health"; then
echo "PASS: failure path handled correctly"
fiRegistrando Chamadas de Stubs para Verificação
Às vezes, precisa verificar não apenas o que seu script produz, mas também como ele chamou uma ferramenta externa — os argumentos que passou, quantas vezes ela foi chamada ou em que ordem. Um stub espião registra suas invocações em um arquivo.
Após o teste, seu ambiente de testes lê o arquivo de registro e verifica o conteúdo. Isso permite uma verificação em nível de argumentos sem nenhuma estrutura especial.
#!/usr/bin/env bash
# Spy stub: record every invocation of 'aws' to a log file
STUB_BIN=$(mktemp -d)
CALL_LOG=$(mktemp)
trap 'rm -rf "$STUB_BIN" "$CALL_LOG"' EXIT
export PATH="$STUB_BIN:$PATH"
export CALL_LOG # make it available inside the stub
cat > "$STUB_BIN/aws" << 'EOF'
#!/usr/bin/env bash
# Append all arguments to the call log
echo "aws $*" >> "$CALL_LOG"
# Return fake S3 output
echo "upload: ./report.pdf to s3://my-bucket/report.pdf"
exit 0
EOF
chmod +x "$STUB_BIN/aws"
# Simulate the script under test calling aws s3 cp
aws s3 cp report.pdf s3://my-bucket/report.pdf
aws s3 cp logs.tar.gz s3://my-bucket/logs.tar.gz
# Verify calls were made with expected arguments
echo "--- Recorded calls ---"
cat "$CALL_LOG"
grep -q 's3://my-bucket/report.pdf' "$CALL_LOG" && echo "PASS: S3 upload verified"Criando Stubs para Vários Comandos de Uma Vez
Um script real costuma chamar várias ferramentas externas. Pode criar stubs para todas elas no mesmo diretório STUB_BIN. Cada arquivo de stub é independente e pode retornar saídas e códigos de saída diferentes.
Mantenha os stubs mínimos: retorne apenas o que o script em teste realmente analisa. Não tente simular todas as opções — apenas o subconjunto usado pelo seu script.
#!/usr/bin/env bash
# Stub both 'git' and 'docker' for a release script test
STUB_BIN=$(mktemp -d)
trap 'rm -rf "$STUB_BIN"' EXIT
export PATH="$STUB_BIN:$PATH"
# Stub git: pretend we are on tag v2.1.0
cat > "$STUB_BIN/git" << 'EOF'
#!/usr/bin/env bash
case "$*" in
*"describe --tags"*) echo "v2.1.0" ;;
*"rev-parse HEAD"*) echo "abc1234" ;;
*) echo "[git stub] unhandled: $*" >&2 ; exit 1 ;;
esac
EOF
chmod +x "$STUB_BIN/git"
# Stub docker: pretend build and push succeed
cat > "$STUB_BIN/docker" << 'EOF'
#!/usr/bin/env bash
echo "[docker stub] $*"
exit 0
EOF
chmod +x "$STUB_BIN/docker"
# Simulate the release logic
VERSION=$(git describe --tags)
SHA=$(git rev-parse HEAD)
echo "Building image for version=$VERSION sha=$SHA"
docker build -t "myapp:$VERSION" .
docker push "myapp:$VERSION"Usando Funções como Stubs (Sem Precisar de Arquivos)
Em casos simples, não precisa escrever arquivos. Pode definir uma função do shell com o mesmo nome do comando. Como as funções são resolvidas antes da busca externa em PATH, elas ganham prioridade automaticamente.
Esta é a abordagem mais rápida para testar scripts carregados com source. No entanto, os stubs de função só funcionam dentro do mesmo processo do shell — eles não ficam visíveis para subprocessos iniciados com bash -c explicitamente nem para processos executados em segundo plano. Nesses casos, use a abordagem baseada em arquivos.
#!/usr/bin/env bash
# Source the script under test (a small helper library)
source_under_test() {
# Inline the logic we want to test
get_instance_id() {
# Would normally call: curl http://169.254.169.254/latest/meta-data/instance-id
curl -sf http://169.254.169.254/latest/meta-data/instance-id
}
}
source_under_test
# Override curl with a shell function stub
curl() {
echo "i-0abc123def456"
return 0
}
# Export is NOT needed — function is visible in same shell
# Run the function under test
result=$(get_instance_id)
[[ "$result" == "i-0abc123def456" ]] && echo "PASS: instance ID returned" || echo "FAIL"Exportando Funções para Subprocessos
Quando o script em teste inicia um subprocesso (por exemplo, bash script.sh ou um pipeline), os stubs de funções do shell definidos no processo pai não são herdados por padrão. Há duas opções:
- Use
export -f function_namepara exportar a função — ela ficará disponível para processos filho dobash - Ou recorra a stubs baseados em arquivos em um diretório
STUB_BIN, que sempre funcionam entre diferentes processos
export -f é elegante, mas funciona apenas com bash (não com sh nem com outros shells). Prefira stubs baseados em arquivos em ambientes de integração contínua poliglotas.
#!/usr/bin/env bash
# Demonstrate export -f for subshell-visible function stubs
# Define the stub in the current shell
curl() {
echo '{"status":"ok"}'
return 0
}
# Export the function so child bash processes inherit it
export -f curl
# Verify the stub works in a subshell
bash -c '
response=$(curl -sf http://api.example.com/status)
echo "Subshell got: $response"
'
# Without export -f, the subshell would call the real curl
# (or fail if curl is not installed)Integrando Stubs a uma Estrutura de Testes (BATS)
Ao usar o BATS (Sistema Automatizado de Testes do Bash), a configuração dos stubs deve ficar no gancho setup(), e a limpeza, em teardown(). O BATS redefine o ambiente entre os testes, portanto cada teste recebe um diretório de stubs novo.
Variáveis do BATS, como $BATS_TEST_TMPDIR, fornecem automaticamente um diretório temporário exclusivo para cada teste — use-o em vez de mktemp -d para manter o código mais limpo.
#!/usr/bin/env bats
# File: test_deploy.bats
# Run with: bats test_deploy.bats
setup() {
# BATS provides a unique tmpdir per test
export STUB_BIN="$BATS_TEST_TMPDIR/stub_bin"
mkdir -p "$STUB_BIN"
export PATH="$STUB_BIN:$PATH"
# Default stub: healthy service
cat > "$STUB_BIN/curl" << 'EOF'
#!/usr/bin/env bash
echo '{"healthy":true}'
EOF
chmod +x "$STUB_BIN/curl"
}
teardown() {
# BATS auto-removes BATS_TEST_TMPDIR, but explicit is safer
rm -rf "$STUB_BIN"
}
@test "deploy succeeds when service is healthy" {
run bash deploy.sh
[ "$status" -eq 0 ]
[[ "$output" == *"Deploy complete"* ]]
}
@test "deploy aborts when service is down" {
# Override the stub for this specific test
echo -e '#!/usr/bin/env bash\nexit 1' > "$STUB_BIN/curl"
chmod +x "$STUB_BIN/curl"
run bash deploy.sh
[ "$status" -ne 0 ]
}Verificação de Conhecimento: Simulando Comandos no Bash
Teste sua compreensão sobre a simulação de comandos e os padrões de stubs no Bash.
Um desenvolvedor escreve um teste que define uma função do shell chamada aws para substituir o AWS CLI real. O teste funciona normalmente quando executado diretamente no terminal, mas, quando o pipeline de integração contínua executa o script em teste como bash deploy.sh, o stub é ignorado e o comando aws real é chamado.
Qual é a correção correta?
Recapitulação: Simulando Comandos e Criando Stubs para Ferramentas Externas
Você aprendeu o conjunto completo de técnicas para substituir comandos reais por falsificações controladas durante os testes de scripts Bash.
Principais técnicas abordadas:
- Precedência no PATH — crie um diretório
STUB_BINcommktemp -d, escreva nele arquivos de stub executáveis e coloque esse diretório no início dePATH - Códigos de saída dos stubs — retorne
0em caso de sucesso e valores diferentes de zero para simular falhas específicas (erros de rede, permissão negada etc.) - Stubs espiões — acrescente os argumentos a um arquivo de registro dentro do stub para verificar como seu script chamou as ferramentas externas
- Vários stubs — coloque vários arquivos de stub no mesmo
STUB_BINpara simular de uma só vez todo um ecossistema de dependências - Stubs de função — defina uma função do shell com o mesmo nome de um comando para simulação no mesmo processo; use
export -fpara alcançar processos filho do bash - Integração com BATS — use os ganchos
setup()/teardown()e$BATS_TEST_TMPDIRpara obter isolamento limpo em cada teste
Sempre use um trap '...' EXIT para garantir a limpeza dos stubs, independentemente do resultado do teste. Mantenha os stubs mínimos — retorne apenas o que seu script realmente analisa. Stubs baseados em arquivos são a opção mais portável para pipelines de integração contínua.
Perguntas Frequentes
A aula “Simulação de comandos e criação de substitutos para ferramentas externas” é grátis?
Sim — o texto completo de “Simulação de comandos e criação de substitutos para ferramentas externas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
O que vou aprender em “Simulação de comandos e criação de substitutos para ferramentas externas”?
Substitua o PATH e defina binários falsos para testar scripts sem tocar nos sistemas reais. Você pratica Linux Command Line & Bash Scripting Mastery com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Linux Command Line & Bash Scripting Mastery?
Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Simulação de comandos e criação de substitutos para ferramentas externas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Linux Command Line & Bash Scripting Mastery?
Sim. Cada aula de Linux Command Line & Bash Scripting Mastery inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Teste unitário de funções com Bats-core
- Simulação de comandos e criação de substitutos para ferramentas externas
- Dispositivos de teste, ambientes temporários e cobertura
- Execução de testes de Shell em pipelines de integração contínua