This is part ten of a series on how to approach bash programming in a way that’s safer and more structured than your basic script.
See part 1 if you want to catch the series from the start.
Last time, we factored created a test for our support function, sourced. This time, I’ll discuss the idea of test independence and a technique for working with shpec.
The biggest issue with shpec tests is that they all execute in the same context.
For example, if one test defines a variable, that variable exists for that test as well as all of the following tests. Sometimes, a following test may reuse the same variable name but miss assigning a new value, in which case the wrong value may get referenced.
It’s better for the tests to not share the same function or variable namespaces. If they are independent, such a missed assignment would result in an empty value when referenced instead of a wrong value.
That, however, is still not going to cause the mistake to be caught, at least in the case of a variable. An empty value is as bad as a wrong value.
Unset, Match, Game
The first thing to do in our test script is to cause unset values to cause bash to exit with an error message:
While this will cause unset variables to be detected, it will also do the same for the code under test, which may be undesirable. The solution is to either make sure the code under test also doesn’t try to use unset variables or to toggle the setting around the function call under test.
The smart choice is to make sure the code under test also doesn’t try to use unset variables if possible.
She Sells Subshells by the Seashore
The second thing to do in our test script is to sandbox the body of each test in a subshell.
A subshell is a new shell context, or rather an entire process, which is a mirror image of the shell from which it is created. All of the functions and variables of the parent shell are in the subshell.
The subshell has no effect on its parent, however, so when it exits, any changes to variables are gone. You can’t share values from a subshell back to the parent. Therefore if all tests are in their own subshell, they can’t affect each others namespaces.
Let’s use hello_world.shpec as an example:
Subshells are created with parentheses.
This is a fine start, however, it causes issues with shpec. Shpec
relies on the global variable
_shpec_failures to keep track of how
many tests have failed. Because the assert function is the one that
modifies the count, and that function must be called in the subshell,
its change to
_shpec_failures is lost when the subshell ends.
Instead, let’s use
_shpec_failures to count just the errors in the
subshell, then add it to the running count in the parent shell. All we
need to do is to reset it to zero at the outset, then return it as the
last thing in the subshell and add it in the parent.
It’s rather ugly looking, so usually I squash the added commands into single lines. Here’s a final example of the entire file:
Continue with part 11 - strict mode