Search code examples
shellunit-testingbats-core

How to create output in a shell function for unit testing with bats?


After setting up a bats test framework that can test shell scripts in some directory, I am trying to expand the bats tests to test a shell function that is written in another shell file named passive_function.sh.

Test setup

I created a template repository to perform shell testing. It contains a file called test_other_shell.bats in folder /test/ with content:

#!./test/libs/bats/bin/bats

load 'libs/bats-support/load'
load 'libs/bats-assert/load'

@test "running the file in /src/active_function.sh." {
    run ./src/active_function.sh 9 33
    assert_output 42
}

And I created a file called main.sh with content:

#!/bin/sh
some_active_function() {
    sum="$1 + $2" | bc -l
    echo "$sum"
}

some_active_function

Test result

Running the bats test from terminal fails with output:

 ✗ running the file in /src/active_function.sh.
   (from function `assert_output' in file test/libs/bats-assert/src/assert.bash, line 239,
    in test file test/test_active_function.bats, line 8)
     `assert_output 42' failed
   
   -- output differs --
   expected : 42
   actual   : 
   --

Function verification

I manually verified this function returns 42 by running:

source ./src/active_function.sh; some_active_function 9 33

from terminal, which prints:

42
42

Question

How can I write a bats test that tests a shell function which returns the addition of input 2 integer numbers?

E.g. if I call somefunc 3 5 the test pass if the function returns 8, and fail otherwise.


Solution

  • sum="$1 + $2" | bc -l
    

    does not set the sum variable to the sum of the numbers:

    • the pipe means that bash runs each command in the pipeline in a separate subshell.
    • the first subshell gets sum assigned the string "9 + 33", and there's no output
    • the second subshell starts bc with an empty string sent to it's stdin, so bc outputs nothing
    • the sum variable in the function's scope remains uninitialized.

    Next, when you execute that code, you define the function and then invoke it with no parameters. You need the last line to be

    some_active_functionm "$@"
    

    Also, if you want to test the function you don't really need to run a script:

    @test "running the file in /src/active_function.sh." {
        source ./src/active_function.sh
        output=$(some_active_function 9 33)
        ((output == 42))
    }
    

    Untested. You could probably get away with

    load ./src/active_function.sh
    
    @test "running the file in /src/active_function.sh." {
        output=$(some_active_function 9 33)
        ((output == 42))
    }