barsh

For stubbing CLI commands to test your wild and crazy shell scripts


License
ISC
Install
npm install barsh@0.0.1

Documentation

barsh

For stubbing CLI tools to test your wild and crazy shell scripts

Why should I use this?

  • I am happier when I write tests in JavaScript using tape or tap.
  • The shell script that I need to test integrates one or more CLI tools that perform I/O or long-running processes.
  • I want to stub those CLI tools because I'm confident that they're independently well tested and I understand their interface enough to mock it.

What does it do?

It runs the shell script that you want to test via child_process.exec. Before it does that, it takes a bunch of stub scripts that you've written and places them in a temporary directory. It adds that directory to the $PATH that the child process will have access to. So, your stub scripts get called instead of the real CLI tools.

It also gives you a way to make assertions from within your stubbed scripts about the expected arguments that the function was called with.

What does it look like?

Say that the script I want to test is a file called download-latest.sh and looks like this:

#!/usr/bin/env bash

bucket=${1}
prefix=${2}
destination=${3}

latest=$(aws s3 cp s3://${bucket}/${prefix}/latest -)
aws s3 cp s3://${bucket}/${prefix}/${latest} ${destination}

When I test this script, I am confident that as long as I provide aws with the right arguments, it will do what I expect it to do. However I don't want to have to reach out an talk to real-life S3 every time I run the test. So I can write a test like this:

var test = require('tape');
var barsh = require('barsh');
var fs = require('fs');

test('[my script] downloads the latest file', function(assert) {
  var testBucket = 'my-bucket';
  var testPrefix = 'some-prefix';
  var fixture = './fixtures/file';
  var destination = './file-received';

  var stubs = {};
  stubs.aws = `
    #!/usr/bin/env bash

    assert equal $1 s3 "calls aws s3"
    assert equal $2 cp "calls aws s3 cp"

    if [[ "$3" == *latest ]]; then
      assert equal $3 s3://${testBucket}/${testPrefix}/latest "request the correct latest file"
      assert equal $4 - "pipes latest file to stdout"
      echo "contents-of-latest-file"
    else
      assert equal $3 s3://${testBucket}/${testPrefix}/contents-of-latest-file "downloads the correct file"
      cp ${fixture} $4
    fi
  `;

  var command = `../scripts/download-latest.sh ${testBucket} ${testPrefix} ${destination}`;

  barsh(assert).exec(command, stubs, function(err, stdout, stderr) {
    assert.ifError(err, 'completed without error');
    var downloaded = fs.readFileSync(destination, 'utf8');
    var expected = fs.readFileSync(fixture, 'utf8');
    assert.equal(downloaded, expected, 'downloaded latest file');
    assert.end();
  });
});

Running this example test will give a console output like this:

TAP version 13
# [my script] downloads the latest file
ok 1 calls aws s3
ok 2 calls aws s3 cp
ok 3 request the correct latest file
ok 4 pipes latest file to stdout
ok 5 calls aws s3
ok 6 calls aws s3 cp
ok 7 downloads the correct file
ok 8 completed without error
ok 9 downloaded latest file

1..9
# tests 9
# pass  9

# ok