Having briefly explained what a test is we can come to an inevitable conclusion that having only a single test is never enough. We usually write a lot of them and it is useful to group them.
Assuming that the tests we are using conform to the contract of returning proper exit statuses, the whole group can technically be treated like a single test.
Let’s put them inside a
test -f file-a.txt test -f file-b.txt test -f file-c.txt
Now, (I assume that the script is an executable file) let’s run it:
./tests.sh echo $? # returns: 1
All is well. The required files do not exist, so the test is failing.
Create the files one by one and run the test each time:
touch file-a.txt ./tests.sh echo $? # returns: 1 touch file-b.txt ./tests.sh echo $? # returns: 1 touch file-c.txt ./tests.sh echo $? # returns: 0
All is well again. The test passes.
Now, let’s make sure the test is solid. Delete the first file and run the test again.
rm file-a.txt ./tests.sh echo $? # returns: 0
Wait, what? The test returns
0? It should fail!
The thing is that when we put some commands into a script, by default, only the last one’s return status is returned by the script itself.
To ensure that the script will exit when a command within that script exits with a non-zero status, we use the
bash builtin with the
#!/bin/bash (just to make sure we will be running Bash) on the first line and
set -e on the second line of the test script and run it yet again.
Now the test fails as it should.
Run a series of tests to ensure all relevant situations are being checked.
rm file-?.txt ./tests.sh echo $? # should return 1 touch file-a.txt touch file-b.txt touch file-c.txt ./tests.sh echo $? # should return 0 rm file-a.txt ./tests.sh echo $? # should return 1 touch file-a.txt rm file-b.txt ./tests.sh echo $? # should return 1 touch file-b.txt rm file-c.txt ./tests.sh echo $? # should return 1
This shows the general idea behind testing.