theboocock / shunit2

Automatically exported from code.google.com/p/shunit2
0 stars 0 forks source link

Can't find a forum to post questions to so here it is... #19

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hi Kate,

Great to see you've created a unit test framework for shell but I'm
having a hard time finding any examples beyond those in log4sh and
shflags.  I've previously been writing unit tests for Java and using
Mockito for mockingn all external dependencies.  I'm now
trying to use those same techniques when testing my scripts with shunit2
and would like your suggestions for patterns to test them.

I've listed here examples of two tests which I have created along with
my thoughts on how I've written them and would be very interested to
hear how you would go about it.  I've got a suspicion that there
should be an easier way of doing some of these.

For testing the first example (below) I need to have
* a directory which will need to exist
* a call to 'make'

My initial approach
* the test to set SOURCE to point to a sandbox testing area which
contains the required directories, in this case 'buildarea'.  This
works ok.
* create a mocked 'Makefile' (see below) inside '${SOURCE}/buildarea'
which outputs the command line options to a file 'Makefile.output'.
Checking that this file exist confirms that 'make' was run
successfully and further checking of the content of this file
validates that 'make' was given the correct parameters.

This seemed to work ok so I moved onto writing my second test

---------------------------------------- First Example
----------------------------------------
-- Code being tested --

function make_kernel() {
       infoecho "Making kernel..."

       VARIANT_DIRECTORY="${SOURCE}/buildarea"
       cd ${VARIANT_DIRECTORY}

       make O=${OBJECT_DIRECTORY}
       RESULT=$?

       if [[ ${RESULT} != 0 ]]; then
               infoecho "Make kernel, calling make failed. Exiting"
               exit ${RESULT}
       fi
}

-- Test Code --

testMakeKernel()
{
       export SOURCE=${whereAmI}/fakedirectories/mwfake
       export OBJECT_DIRECTORY="1234"

       # Remove the output file which will be generated from the
Makefile
       #
       outputfile="${SOURCE}/buildarea"
       rm "${outputfile}"

       make_kernel

       result=$?
       assertTrue "make failed" "[ $result = 0 ]"

       # Make sure the output file exists
       #
       assertTrue "make was called" "[ -f ${outputfile} ]"
       result=`cat ${outputfile}`

       # Make sure it has the correct parameter inside it
       #
       expecting="O:1234"
       assertTrue "Makefile result: expected '${expecting}', but got
'${result}'" "[ '${result}' = '${expecting}' ]"
}

-- Mock Makefile --

default:
       @echo "O:$(O)" > Makefile.output

---------------------------------------- End Of First Example
----------------------------------------

For testing the second example (below) I need to have
* a call to an external command, in this case 'find'
* know data coming back from find
* a call to another external command 'tar'

Now with this test I would like to
* Mock the find command
* Mock the tar command
* Count command invocations to 'find' and 'tar'

My approach was to create a set of mock functions for tar and find
which would produce the required fake data.  This would entail setting
a variable MOCK_FIND_RESULT="directory/file1.spk.tar.gz directory/
file2.spk.tar.gz" so that when 'find' is called it uses
MOCK_FIND_RESULT to produce the result.  This also seemed to work ok.

function find()
{
       # Return preset mock results
       #
       echo "${MOCK_FIND_RESULT}"

       return ${MOCK_FIND_EXIT_STATUS:-0}
}

I have also done the same thing with tar

function tar()
{
       return ${MOCK_TAR_EXIT_STATUS:-0}
}

Counting the number of invocations has however proved tricky and I'm
still working on it.  Basically I added a counter variable inside the
functions like so

function tar()
{
               TARCOUNT=$(expr ${TARCOUNT} + 1)
       return ${MOCK_TAR_EXIT_STATUS:-0}
}

This looks to work fine until we run our test as the code being tested
has the 'tar' command inside the 'for in find' this produces a pipe,
hence a seperate shell.  The counter variable is now incremented in
this additional shell and therefore has no effect on the test
variables.

-------------------------------------------- Second Example
------------------------------------------
-- Code being tested --
function extract_spk_tarballs() {
       infoecho "extract_spk_tarballs: source is ${SOURCE}"
       for filename in `find "${SOURCE}" -name "*spk*.tar.*"`;
       do
               dir=`dirname "${filename}"`

               infoecho "Extracting ${filename}"
               tar xf "$filename" -C "${dir}"
               RESULT=$?

               if [[ ${RESULT} != 0 ]]; then
                       infoecho "Extract spk tarballs, tar failed.
Exiting"
                       exit ${RESULT}
               fi
       done
}

-- Test Code --
test_extract_spk_tarballs() {
       export SOURCE=${whereAmI}/fakedirectories/mwfake

       # Setup two fake results for spk tarballs
       #
       export MOCK_FIND_RESULT="fakedir/file1.spk.tar.gz fakedir/
file2.spk.tar.gz"

       extract_spk_tarballs
       result=$?
       assertTrue "extract_spk_tarballs failed" "[ $result = 0 ]"

       # Can't guarantee that we can count invocations. Setting a
variable in a pipe won't work
       #
       #validateCallCount "tar" "2" "${TARCOUNT}"
}
---------------------------------------- End Of Second Example
---------------------------------------

-- Additional Thoughs --
Instead of creating methods for each mocked command I could use alias
instead, at least for mocking some of them.  This would mean that the
mock commands would be unique and data specific for each test which
would be much better

               alias find="echo fakedir/file1.spk.tar.gz
fakedir/file2.spk.tar.gz"

Original issue reported on code.google.com by leemil...@gmail.com on 9 Feb 2010 at 4:24

GoogleCodeExporter commented 9 years ago
*Very* belated update... The users lists can be found here. 
http://groups.google.com/group/shunit2-users

Did you ever get this working? I can look through and make recommendations if 
you didn't.

Original comment by kate.war...@gtempaccount.com on 15 Mar 2011 at 12:29