I have a shell script that I would like to test with shUnit. The script (and all the functions) are in a single file since it makes installation much easier.
Example for script.sh
#!/bin/sh foo () { ... } bar () { ... } code
I wanted to write a second file (that does not need to be distributed and installed) to test the functions defined in script.sh
Something like run_tests.sh
#!/bin/sh . script.sh # Unit tests
Now the problem lies in the .
(or source
in Bash). It does not only parse function definitions but also executes the code in the script.
Since the script with no arguments does nothing bad I could
. script.sh > /dev/null 2>&1
but I was wandering if there is a better way to achieve my goal.
Edit
My proposed workaround does not work in the case the sourced script calls exit
so I have to trap the exit
#!/bin/sh trap run_tests ERR EXIT run_tests() { ... } . script.sh
The run_tests
function is called but as soon as I redirect the output of the source command the functions in the script are not parsed and are not available in the trap handler
This works but I get the output of script.sh
:
#!/bin/sh trap run_tests ERR EXIT run_tests() { function_defined_in_script_sh } . script.sh
This does not print the output but I get an error that the function is not defined:
#!/bin/sh trap run_tests ERR EXIT run_tests() { function_defined_in_script_sh } . script.sh | grep OUTPUT_THAT_DOES_NOT_EXISTS
This does not print the output and the run_tests
trap handler is not called at all:
#!/bin/sh trap run_tests ERR EXIT run_tests() { function_defined_in_script_sh } . script.sh > /dev/null
To invoke a function, simply use the function name as a command. To pass parameters to the function, add space separated arguments like other commands. The passed parameters can be accessed inside the function using the standard positional variables i.e. $0, $1, $2, $3 etc.
source is a shell built-in command which is used to read and execute the content of a file(generally set of commands), passed as an argument in the current shell script. The command after taking the content of the specified files passes it to the TCL interpreter as a text script which then gets executed.
To invoke a bash function, simply use the function name. Commands between the curly braces are executed whenever the function is called in the shell script. The function definition must be placed before any calls to the function.
According to the “Shell Builtin Commands” section of the bash manpage, .
aka source
takes an optional list of arguments which are passed to the script being sourced. You could use that to introduce a do-nothing option. For example, script.sh
could be:
#!/bin/sh foo() { echo foo $1 } main() { foo 1 foo 2 } if [ "${1}" != "--source-only" ]; then main "${@}" fi
and unit.sh
could be:
#!/bin/bash . ./script.sh --source-only foo 3
Then script.sh
will behave normally, and unit.sh
will have access to all the functions from script.sh
but will not invoke the main()
code.
Note that the extra arguments to source
are not in POSIX, so /bin/sh
might not handle it—hence the #!/bin/bash
at the start of unit.sh
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With