0

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.

2 Answers 2

1
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))
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the more efficient solution that does not require running a(n entire) script, but allows to test separate methods directly! That can make debugging a lot faster.
0

As indicated in the answer by glenn jackman, I did not pass the incoming arguments to the function in the last line of the function. Furthermore, my summation method was incorrect. A solution (and non-arithmatic example) is included below.

Test and function: adding two numbers

A shell script with a function that adds two numbers named active_function_addition.sh with content:

#!/bin/sh
some_active_function() {
    sum=$(expr "$1" + "$2")
    echo $sum
}
some_active_function "$@"

And a unit test in the form of a bats file, with name test_active_function_addition.bats and content:

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

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

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

Test and function: string manipulation

A shell script with a function that changes upper case characters to lower case characters named active_function_string_manipulation.sh with content:

##################################################################
# Purpose: Converts a string to lower case
# Arguments:
#   $@ -> String to convert to lower case
##################################################################
function to_lower() 
{
    local str="$@"
    local output
    output=$(tr '[A-Z]' '[a-z]'<<<"${str}")
    echo $output
}
to_lower "$@"

And a unit test in the form of a bats file, with name test_active_function_addition.bats and content:

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

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

@test "running the file in /src/active_function_string_manipulation.sh." {
    input="This Is a TEST"
    run ./src/active_function_string_manipulation.sh "This Is a TEST"
    assert_output "this is a test"
}

Test results

Both tests pass.

Notes

  1. During the debugging and testing I determined I missed more relevant knowledge. When I ran the source ./src/active_function.sh; some_active_function 9 33 I retrieved output 42 twice, at some point in time. However, since I did not actually passed the input arguments to the function, the actual return would be void in a clean setting (e.g. the unit test). When I closed the terminal and re-opened the terminal I did indeed receive the void return on the manual test line. I think that during the manual tries of different summing methods that some variables were stored in memory without me being aware of that which resulted in the 42 being displayed without the arguments being passed. I did not reproduce this behaviour in a controlled manner.

  2. This solution requires the entire shell script to be run, which might take a lot of time. Glenn jackman gave an answer with a more elegant and more efficient solution that only runs a specific function without having to run a complete script.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.