Testing Bash Scripts with BATS: A Practical Guide

Testing Bash Scripts with BATS: A Practical Guide


images/testing-bash-scripts-with-bats--a-practical-guide.webp

Bash, the ubiquitous shell scripting language, is a staple in the toolkit of any developer, especially those working in a Unix-like environment. While Bash scripts are often used for automating tasks, their testing is sometimes overlooked. This is where BATS (Bash Automated Testing System) comes into play, providing a simple yet effective way to test Bash scripts. In this post, we’ll delve into the practicalities of using BATS, exploring its features and how it can help ensure the reliability and security of your Bash scripts.

Why Test Bash Scripts?

Before diving into BATS, let’s address the elephant in the room: why bother testing Bash scripts? The answer is simple: reliability and security. Bash scripts often handle critical tasks like deploying software, managing files, or configuring systems. A small error can lead to significant issues, from broken builds to security vulnerabilities. Testing these scripts is not just good practice; it’s a necessity.

Introduction to BATS

BATS is a TAP-compliant testing framework for Bash scripts. It allows you to write tests for your scripts in a straightforward and readable manner. BATS tests are written in Bash, so you don’t need to learn a new language, and they can be run on any system with Bash and BATS installed.

Setting Up BATS

To start using BATS, you need to install it. It’s available on most Unix-like systems and can be installed via package managers like brew on macOS or apt on Ubuntu:

# macOS
brew install bats-core

# Ubuntu
sudo apt-get install bats

Writing Your First Test

Let’s create a simple Bash script named greet.sh:

#!/bin/bash

function greet() {
    echo "Hello, $1!"
}

This script defines a function greet that takes a name and prints a greeting. To test this, we’ll create a BATS test file named test_greet.bats:

#!/usr/bin/env bats

load './greet.sh'

@test "greet function with name" {
  run greet "World"
  [ "$status" -eq 0 ]
  [ "$output" = "Hello, World!" ]
}

This test checks if the greet function returns the expected string and exits with a status of 0 (success).

Running Tests

To run your BATS tests, use the bats command:

bats test_greet.bats

Testing Edge Cases

Testing edge cases is crucial. Let’s add a test for when no name is provided:

@test "greet function without name" {
  run greet ""
  [ "$status" -eq 0 ]
  [ "$output" = "Hello, !" ]
}

This test ensures the script handles empty input gracefully.

Advanced Testing: Mocks and Stubs

Sometimes, you might need to test scripts that interact with external systems. Mocks and stubs can simulate these interactions. For example, if your script makes a network request, you could mock the command responsible for it.

Suppose greet.sh now has a function that makes a network call:

function fetch_greeting() {
    curl http://example.com/greeting
}

You can mock curl in your test:

@test "fetch_greeting function" {
  # Mocking curl
  function curl() {
    echo "Mocked greeting"
  }

  run fetch_greeting
  [ "$output" = "Mocked greeting" ]
}

This test replaces the real curl command with a mock function, ensuring your test is not dependent on external factors.

Continuous Integration

Integrating BATS tests into your CI pipeline ensures that your scripts are always tested automatically. Most CI systems, like Jenkins or GitHub Actions, allow you to run BATS tests as part of your build process.

Conclusion

Testing Bash scripts with BATS is not just a best practice; it’s essential for ensuring the reliability and security of your scripts. BATS' simplicity and flexibility make it an ideal choice for Bash script testing. By incorporating BATS into your development workflow, you’re taking a significant step towards more robust and secure scripting. Remember, the cost of fixing a bug exponentially increases the later it’s found; proactive testing with tools like BATS is a key strategy in mitigating this risk.


About PullRequest

HackerOne PullRequest is a platform for code review, built for teams of all sizes. We have a network of expert engineers enhanced by AI, to help you ship secure code, faster.

Learn more about PullRequest

PullRequest headshot
by PullRequest

December 28, 2023