One slightly harebrained idea for "unit testing" shell scripts would be to have a wrapper script which executes your script in a controlled environment - namely that it defines functions which "mock" the real commands you don't want to execute (e.g. ssh() { }
).
This would allow you to test the interactions you're interested in, without worrying about the other processes you're calling out to, as brainzzy describes. This is still very dependent on well designed bash code, but at least in theory it'd be possible, I think.
Demo:
$ cat /tmp/grep.sh
#!/bin/bash
# Simple program that relies on grep
echo -e "This is a test\nThis line doesn't match." | grep test
Unit Tester:
$ cat /tmp/unittest.sh
#!/bin/bash
grep() {
echo "Mock GREP result"
}
. "$@"
Now if we run grep.sh
directly, it searches as expected:
$ /tmp/grep.sh
This is a test
But if run from within the unittest script the grep
is mocked by the function:
$ /tmp/unittest.sh /tmp/grep.sh
Mock GREP result
Allowing us to test whatever behavior we're trying to verify.
This has several limiting factors, like needing to be run from the same shell (the .
command) meaning that if the script in turn calls other scripts, they will again be calling the real commands.
An alternative would be to define a set of scripts in a unit test directory, e.g.
$ ls /usr/local/unitbin
grep
ssh
svn
Then have the unit test script change the PATH the script runs from, e.g.:
$ cat /tmp/unittest.sh
#!/bin/bash
PATH=/usr/local/unitbin:$PATH "$@"
This should work for scripts that call other scripts in turn.
Both of these examples, like I said, are slightly ridiculous, and potentially more trouble than they're worth. I would definitely look to the other answers to this question before considering this path. But if you have bash code that you'd like to unit test, but can't run in a sandbox safely, one of these options might work for you.