0Pricing
Linux Command Line & Bash Scripting Mastery · Lección

Simulación de comandos y sustitución de herramientas externas

Modifique PATH y defina binarios falsos para probar scripts sin interactuar con sistemas reales.

Simulación de comandos y sustitución de herramientas externas es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

¿Por qué simular comandos en las pruebas de Bash?

Cuando prueba un script de Bash que llama a curl, aws, git o cualquier herramienta externa, se enfrenta a un problema: las llamadas reales acceden a la red, modifican el estado, cuestan dinero o simplemente fallan en un entorno de CI donde esas herramientas no están instaladas.

Simular significa sustituir el comando real por uno falso que usted controla. Su elemento falso (el stub) devuelve una salida y unos códigos de salida predecibles, por lo que la prueba es rápida, aislada y reproducible.

  • No se necesita acceso a la red ni a la nube
  • Las pruebas se ejecutan en milisegundos en lugar de segundos
  • Puede simular errores difíciles de provocar en sistemas reales
  • Los pipelines de CI se mantienen limpios y sin dependencias

Bash ofrece un mecanismo sorprendentemente sencillo para hacerlo: coloque el binario falso en una ubicación anterior a la del binario real dentro de $PATH.

Cómo funciona la búsqueda en PATH

Cuando el shell ejecuta un comando como curl, busca en cada directorio de $PATH, de izquierda a derecha, y ejecuta la primera coincidencia que encuentra.

Esto significa que, si antepone un directorio que contiene su propio script curl, el shell nunca llegará a /usr/bin/curl.

El patrón de sustitución:

  1. Cree un directorio temporal (su stub bin)
  2. Escriba un ejecutable falso con el mismo nombre que el comando real
  3. Anteponha ese directorio a PATH
  4. Ejecute el script que se va a probar; este llamará a su stub, no al binario real
  5. Limpie el directorio temporal después de la prueba

Esto funciona sin acceso root, sin modificar archivos del sistema y sin ningún framework especial.

Crear un directorio para stubs

El patrón estándar usa mktemp -d para crear un directorio temporal aislado para los stubs. Cada prueba o suite de pruebas obtiene su propio directorio, lo que evita la contaminación entre pruebas.

Cuando termine la prueba, elimine el directorio con rm -rf. Usar un trap garantiza la limpieza incluso si la prueba finaliza antes de tiempo debido a un error.

#!/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."

Escribir su primer stub

Un stub no es más que un archivo ejecutable con el mismo nombre que el comando que desea sustituir. Imprime la salida que espera el script que se está probando y termina con el código que usted elija.

Reglas principales para los stubs:

  • El archivo debe ser ejecutable (chmod +x)
  • La línea de shebang (#!/usr/bin/env bash) es obligatoria
  • Imprima la salida que analizaría su script real
  • Use exit 0 para indicar éxito y un valor distinto de cero para simular fallos
#!/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/version

Probar un script que llama a curl

Ahora vamos a juntar todo. Suponga que tiene un script de despliegue que llama a curl para comprobar un endpoint de estado y que termina con un error si el servicio no está operativo. Quiere probar tanto el caso esperado como el caso de error sin usar un 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"

Simulación de fallos de comandos

Uno de los usos más valiosos de los stubs es simular fallos que son difíciles de reproducir con herramientas reales: tiempos de espera de red, errores de permisos, disco lleno o una API remota que devuelve un 500.

Para simular un fallo, haga que el stub termine con un código distinto de cero. También puede escribir en stderr exactamente como lo haría el comando real, para probar por completo el manejo de errores de su script.

#!/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"
fi

Registro de llamadas a stubs para su verificación

A veces necesita comprobar no solo qué muestra su script, sino también cómo llamó a una herramienta externa: los argumentos que le pasó, cuántas veces la llamó o en qué orden. Un stub espía registra sus invocaciones en un archivo.

Después de la prueba, su entorno de pruebas lee el archivo de registro y comprueba su contenido. Así obtiene una verificación a nivel de argumentos sin ningún framework 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"

Simulación de varios comandos a la vez

Un script real suele llamar a varias herramientas externas. Puede simularlas todas en el mismo directorio STUB_BIN. Cada archivo de stub es independiente y puede devolver salidas y códigos de salida diferentes.

Mantenga los stubs mínimos: devuelva únicamente lo que el script que está probando realmente analiza. No intente simular todas las opciones; limítese al subconjunto que utiliza su 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"

Uso de funciones como stubs (sin archivos)

En casos sencillos, no necesita escribir ningún archivo. Puede definir una función de shell con el mismo nombre que el comando. Como las funciones se resuelven antes de buscar ejecutables externos en PATH, tienen prioridad automáticamente.

Este es el enfoque más rápido para realizar pruebas unitarias de scripts cargados con source. Sin embargo, los stubs basados en funciones solo funcionan dentro del mismo proceso de shell; no estarán disponibles para subshells iniciados explícitamente con bash -c ni para procesos ejecutados en segundo plano. En esos casos, use el enfoque basado en archivos.

#!/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"

Exportación de funciones a subshells

Cuando el script que está probando inicia un subshell (por ejemplo, bash script.sh o una tubería), los stubs de funciones de shell definidos en el proceso padre no se heredan de forma predeterminada. Tiene dos opciones:

  • Use export -f function_name para exportar la función; estará disponible para los procesos hijos de bash
  • O recurra a stubs basados en archivos en un directorio STUB_BIN, que siempre funcionan entre distintos procesos

export -f es elegante, pero solo funciona con bash (no con sh ni con otros shells). En entornos de CI políglotas, dé preferencia a los stubs basados en archivos.

#!/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)

Integración de stubs con un framework de pruebas (BATS)

Cuando utiliza BATS (Bash Automated Testing System), la configuración de los stubs debe hacerse en el hook setup() y la limpieza, en teardown(). BATS restablece el entorno entre pruebas, por lo que cada prueba obtiene un directorio de stubs nuevo.

Las variables de BATS, como $BATS_TEST_TMPDIR, le proporcionan automáticamente un directorio temporal para cada prueba; úselo en lugar de mktemp -d para obtener un código más limpio.

#!/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 ]
}

Comprobación de conocimientos: simulación de comandos en Bash

Compruebe su comprensión de la simulación de comandos y los patrones de stubs en Bash.

Un desarrollador escribe una prueba que define una función de shell llamada aws para simular el AWS CLI real. La prueba funciona correctamente directamente en la terminal, pero cuando la canalización de CI ejecuta el script que se está probando como bash deploy.sh, el stub se ignora y se llama al comando aws real.

¿Cuál es la solución correcta?

Repaso: simulación de comandos y stubs para herramientas externas

Ha aprendido el conjunto completo de herramientas para reemplazar comandos reales por simulaciones controladas durante las pruebas de scripts de Bash.

Técnicas principales:

  • Anteponer una ruta a PATH: cree un directorio STUB_BIN con mktemp -d, escriba allí archivos de stub ejecutables y anteponga el directorio a PATH
  • Códigos de salida de los stubs: devuelva 0 para indicar éxito y valores distintos de cero para simular fallos concretos (errores de red, permiso denegado, etc.)
  • Stubs espía: añada los argumentos a un archivo de registro dentro del stub para comprobar cómo su script llamó a las herramientas externas
  • Varios stubs: coloque varios archivos de stub en el mismo STUB_BIN para simular de una vez todo un ecosistema de dependencias
  • Stubs de funciones: defina una función de shell con el mismo nombre que un comando para simularlo dentro del mismo proceso; use export -f para llegar a procesos hijos de Bash
  • Integración con BATS: use los hooks setup()/teardown() y $BATS_TEST_TMPDIR para aislar correctamente cada prueba

Use siempre un trap '...' EXIT para garantizar la limpieza de los stubs independientemente del resultado de la prueba. Mantenga los stubs mínimos: devuelva únicamente lo que su script analiza realmente. Los stubs basados en archivos son la opción más portable para las canalizaciones de CI.

Preguntas frecuentes

¿La lección «Simulación de comandos y sustitución de herramientas externas» es gratis?

Sí — el texto completo de «Simulación de comandos y sustitución de herramientas externas» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

¿Qué aprenderé en «Simulación de comandos y sustitución de herramientas externas»?

Modifique PATH y defina binarios falsos para probar scripts sin interactuar con sistemas reales. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?

No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Simulación de comandos y sustitución de herramientas externas»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?

Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Pruebas unitarias de funciones con Bats-core
  2. Simulación de comandos y sustitución de herramientas externas
  3. Fixtures, entornos temporales y cobertura
  4. Ejecución de pruebas de shell en pipelines de CI
← Volver a Linux Command Line & Bash Scripting Mastery