Bats Operations: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(41 intermediate revisions by the same user not shown)
Line 5: Line 5:
=Best Practices=
=Best Practices=


==<span id='Script_to_Run_all_.bats_Tests_from_a_Directory'></span>Script to Run all .bats Tests from a Directory or Multiple Directories==
==<span id='Script_to_Run_all_.bats_Tests_from_a_Directory_or_Multiple_Directories'></span><span id='Script_to_Run_all_.bats_Tests_from_a_Directory'></span>Script to Recursively Run all .bats Tests in Multiple Directories==
 
The following code will run all tests, even some of the tests fail. At the end of the full run, the <code>failed_files</code> variable will contain the list of files for which there were test failures. If the variable is empty, it means all tests passed.
===Single Directory===
 
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
#!/usr/bin/env bash
function do-test() {
 
  bats --version >/dev/null 2>/dev/null || fail "bats not installed, cannot run tests"
for i in $(dirname $0)/*.bats; do
  local bats_tests_root_dir failed_test_files
    echo "running $(basename ${i}) tests ..."
  bats_tests_root_dir="$(dirname $0)/src/test/bash"
    bats $i
  cd "${bats_tests_root_dir}" || fail "failed to cd to ${bats_tests_root_dir}"
done
  # shellcheck disable=SC2044
</syntaxhighlight>
  for d in $(find . -type d -not -name '.'); do
 
     d=${d#./}
===Multiple Directories===
    pushd ${d} >/dev/null || fail "failed to cd to ${d}"
<syntaxhighlight lang='bash'>
    if ls -- *.bats >/dev/null 2>/dev/null; then
#!/usr/bin/env bash
      # .bats files exist in the directory
 
      echo "running all BATS tests from ${bats_tests_root_dir}/${d} ..."
cd ${dir}
      for i in *.bats; do
 
        echo "running $(basename ${i}) tests ..."
for d in $(find . -type d -not -name '.'); do
        bats ${i} || failed_test_files="${failed_test_files} ${bats_tests_root_dir}/${d}/${i}"
     (
      done
        d=${d#./}
     fi
        echo "running all tests from directory '${d}' ..."
    popd >/dev/null || fail "failed to popd"
        cd ${d};
  done
        for i in *.bats; do
  [[ -n ${failed_test_files} ]] && fail "test failures in${failed_test_files}"
            echo "running $(basename ${i}) tests ..."
}
            bats ${i}
        done
     )
done
</syntaxhighlight>
</syntaxhighlight>


Line 66: Line 60:
       └─ test
       └─ test
           └─ bash
           └─ bash
              │
               ├─ mylib1
               ├─ mylib1
              │    ├─ libraries.bash
              │    ├─ data
              │    │  └─ .gitkeep
              │    ├─ myfunction1.bats
              │    └─ myfunction2.bats
              │
               └─ mylib2
               └─ mylib2
                  ├─ libraries.bash
                  ├─ data
                  │  └─ .gitkeep
                  ├─ myfunction3.bats
                  └─ myfunction4.bats
 
</font>
</font>


* Each library to be tested has its own directory.  
* Each library to be tested has its own directory.  
* Each library-specific directory contains an <code>all-libraries.bash</code>. For more details see [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]].
* Each library-specific directory contains a <code>libraries.bash</code> that lists dependencies, including the library to be tested. For more details on how to compose that file, see [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]] below.
* Each function to be tested has its own file, where various scenarios to test the function are implemented as individual @test functions.
* Each function to be tested has its own <code>.bats</code> file, where various scenarios to test the function are implemented as individual @test functions.
* The library's directory has a "data" subdirectory and a "data/tmp".
* The library's directory has a <code>data</code> subdirectory in which a <code>data/tmp</code> will be created dynamically as needed. For more details see [[#Handling_data|Handling data]] section below.


==Loading a Library to be Tested into a .bats Test==
==Loading a Library to be Tested into a .bats Test==
Line 80: Line 86:
Assuming that the library you want to test is called <code>blue.shlib</code>, create a <code>blue-library.bash</code> file in the directory that contain the .bats tests for the library. If the library is located at a fixed relative location to the .bats test files, use this arrangement:
Assuming that the library you want to test is called <code>blue.shlib</code>, create a <code>blue-library.bash</code> file in the directory that contain the .bats tests for the library. If the library is located at a fixed relative location to the .bats test files, use this arrangement:
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
source ${BATS_TEST_DIRNAME}/[...]/blue.shlib
source "${BATS_TEST_DIRNAME}/[...]/blue.shlib"
</syntaxhighlight>
</syntaxhighlight>
Otherwise, you can use an absolute path, but that is more fragile.
Otherwise, you can use an absolute path, but that is more fragile.
Line 87: Line 93:


<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
load blue-library
load blue-library  


@test "something" {
@test "something" {
Line 94: Line 100:
</syntaxhighlight>
</syntaxhighlight>


If more than one library needs to be loaded, create a <code>all-libraries.bash</code> file that sources all libraries, including the one that needs to be tested:
If more than one library needs to be loaded, create a <code>libraries.bash</code> file that sources all libraries, including the one that needs to be tested:
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
source ${BATS_TEST_DIRNAME}/[...]/lib1.shlib
source "${BATS_TEST_DIRNAME}/[...]/lib1.shlib"
source ${BATS_TEST_DIRNAME}/[...]/lib2.shlib
source "${BATS_TEST_DIRNAME}/[...]/lib2.shlib"
source ${BATS_TEST_DIRNAME}/[...]/lib3.shlib
source "${BATS_TEST_DIRNAME}/[...]/lib3.shlib"
source ${BATS_TEST_DIRNAME}/[...]/blue.shlib
source "${BATS_TEST_DIRNAME}/[...]/blue.shlib"
</syntaxhighlight> and from each BATS test, load it with:
</syntaxhighlight> and from each BATS test, load it with:
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
load all-libraries
load libraries
@test "something" {
@test "something" {
...
...
Line 112: Line 118:
==Loading an Executable Script as Library to be Tested into a .bats Test==
==Loading an Executable Script as Library to be Tested into a .bats Test==


It is possible to test functions from an executable script by loading the executable script as library, even if the script loads external libraries itself, and invokes a <code>main</code> function. We use the same idea described above in the [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]] section, with a few additions. Let's assume there is a script called '''<tt>blue</tt>''' that is executable (contains a <code>main()</code> invocation), sources the '''<tt>bash.shlib</tt>''' library, and contains functions that we would like to unit test:
It is possible to test functions from an executable script by loading the executable script as library, even if the script loads external libraries itself, and invokes a <code>main</code> function. We use the same idea described above in the [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]] section, with a few additions. Let's assume there is a script called <code>blue</code> that is executable (contains a <code>main()</code> invocation), sources the <code>bash.shlib</code> library, and contains functions that we would like to unit test:


<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
Line 133: Line 139:
</syntaxhighlight>
</syntaxhighlight>


We would like to test <code>auxiliary()</code> by loading '''<tt>blue</tt>''' as a library.
We would like to test <code>auxiliary()</code> by loading <code>blue</code> as a library.


For that, the following changes need to be introduced in '''<tt>blue</tt>''':
For that, the following changes need to be introduced in <code>blue</code>:


1. Guard library loading with a <code>BATS_TESTING</code> variable:
1. Guard library loading with a <code>BATS_TESTING</code> variable:
Line 160: Line 166:
...
...
</syntaxhighlight>
</syntaxhighlight>
 
3. Create the <code>libraries.bash</code> library loader in the directory that contains the .bats tests. The library loader is similar to the one described in the section [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]] above. The difference is that it must set the <code>BATS_TESTING</code> to true, load the executable script as library, and also load all libraries required by the executable script:
 
3. Create the '''<tt>all-required-libraries.bash</tt>''' library loader in the directory that contains the .bats tests. The library loader is similar to the one described in the section [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]] above. The difference is that it must set the <code>BATS_TESTING</code> to true, load the executable script as library, and also load all libraries required by the executable script:


<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
Line 170: Line 174:
</syntaxhighlight>
</syntaxhighlight>


4. For each .bats test that wants to test functionality from the executable script, load "all-required-libraries", similarly to how is described in  [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]]:
4. For each .bats test that wants to test functionality from the executable script, load <code>libraries</code>, similarly to how is described in  [[#Loading_a_Library_to_be_Tested_into_a_.bats_Test|Loading a Library to be Tested into a .bats Test]]:


<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
load all-required-libraries
load libraries


@test "something" {
@test "something" {
Line 215: Line 219:
}
}
</syntaxhighlight>
</syntaxhighlight>
==<span id='Testing_Code_that_Reads_stdin'></span>Handling stdin (Testing Code that Reads stdin)==
A tested pattern that works when stdin is pulled from with [[bash read|read]] is to "override" read as such:
<syntaxhighlight lang='bash'>
@test "some test" {
    # bash read "override"
    function read() {
        debug "read($@)"
        local variable_name=$3 # this is the index of the argument that specifies
                              # the variable name. It is valid for "read -p 'something' some_var".
        eval "${variable_name}=\"black\""
    }
    run some_function
    ...
}
</syntaxhighlight>
This behaves as "black" was provided at stdin.


==Handling data==
==Handling data==


A useful pattern is to create a "data" subdirectory in the directory that holds the .bats tests, and then access it from the tests with ${BATS_TEST_DIRNAME}/data/...
A useful pattern is to create a <code>data</code> subdirectory in the directory that holds the .bats tests, and then access it from the tests with <code>${BATS_TEST_DIRNAME}/data/...</code>.
===Data and Temporary Directories===
<syntaxhighlight lang='bash'>
function setup() {
  [[ -d "${BATS_TEST_DIRNAME}/data/tmp" ]] || mkdir -p "${BATS_TEST_DIRNAME}/data/tmp"
}
 
function teardown() {
  rm -rf "${BATS_TEST_DIRNAME}/data/tmp"
}
</syntaxhighlight>


==<span id='.22Overload.22_Functions'></span>"Override" Functions==
==<span id='.22Overload.22_Functions'></span>"Override" Functions==
Line 309: Line 344:
}
}
</syntaxhighlight>
</syntaxhighlight>
 
==Debugging Function Invocations==
==Testing Code that Reads stdin==
 
A tested pattern that works when stdin is pulled from with [[bash read|read]] is to "override" read as such:
 
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
@test "some test" {
function setup() {
 
     [[ -d ${BATS_TEST_DIRNAME}/data/tmp ]] || mkdir "${BATS_TEST_DIRNAME}"/data/tmp
     # bash read "override"
     INVOCATION_FILE=${BATS_TEST_DIRNAME}/data/tmp/invocations.txt
     function read() {
    function some-function() {
 
      echo "$@" >> "${INVOCATION_FILE}"
        debug "read($@)"
      ...
        local variable_name=$3
        eval "${variable_name}=\"black\""
     }
     }
}


    run some_function
function teardown() {
  rm -rf "${BATS_TEST_DIRNAME}"/data/tmp
}


    ...
@test "some test" {
  run ...
  ...
  # shellcheck disable=SC2002
  invocation_count=$(cat "${INVOCATION_FILE}" | wc -l | awk '{print $1}')
  [[ ${invocation_count} == 2 ]]
  first_invocation=$(head -1 "${INVOCATION_FILE}")
  [[ ${first_invocation} == "something" ]]
  second_invocation=$(head -2 "${INVOCATION_FILE}" | tail -1)
  [[ ${second_invocation} == "something else" ]]
}
}
</syntaxhighlight>
</syntaxhighlight>
This behaves as "black" was provided at stdin.
=Running Individual Tests=
==Data and Temporary Directories==
 
Individual tests from a .bats test file can be executed by using the <tt>-f</tt>, <tt>--filter</tt> option, with a regular expression that matches the test(s) names.
 
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
function setup() {
bats ./some-file.bats -f "blue test"
  [[ -d ${BATS_TEST_DIRNAME}/data/tmp ]] || mkdir "${BATS_TEST_DIRNAME}"/data/tmp
</syntaxhighlight>
}


function teardown() {
An empty filter acts as no filter, all tests will be executed:
  rm -rf "${BATS_TEST_DIRNAME}"/data/tmp/*
<syntaxhighlight lang='bash'>
}
bats ./some-file.bats -f ""
</syntaxhighlight>
</syntaxhighlight>



Latest revision as of 21:12, 21 July 2023

Internal

Best Practices

Script to Recursively Run all .bats Tests in Multiple Directories

The following code will run all tests, even some of the tests fail. At the end of the full run, the failed_files variable will contain the list of files for which there were test failures. If the variable is empty, it means all tests passed.

function do-test() {
  bats --version >/dev/null 2>/dev/null || fail "bats not installed, cannot run tests"
  local bats_tests_root_dir failed_test_files
  bats_tests_root_dir="$(dirname $0)/src/test/bash"
  cd "${bats_tests_root_dir}" || fail "failed to cd to ${bats_tests_root_dir}"
  # shellcheck disable=SC2044
  for d in $(find . -type d -not -name '.'); do
    d=${d#./}
    pushd ${d} >/dev/null || fail "failed to cd to ${d}"
    if ls -- *.bats >/dev/null 2>/dev/null; then
      # .bats files exist in the directory
      echo "running all BATS tests from ${bats_tests_root_dir}/${d} ..."
      for i in *.bats; do
        echo "running $(basename ${i}) tests ..."
        bats ${i} || failed_test_files="${failed_test_files} ${bats_tests_root_dir}/${d}/${i}"
      done
    fi
    popd >/dev/null || fail "failed to popd"
  done
  [[ -n ${failed_test_files} ]] && fail "test failures in${failed_test_files}"
}

Exit after the First Test Failure

The above loops do not look if an individual bats command failed, and go through all tests reporting failures. This is variant that fails the overall loop if at least one test fails.

local test_failed=0
    ...
    bats ${i}
    if [ $? -ne 0 ]; then
        test_failed=1
    fi
...
if [ $test_failed -eq 1 ]; then
    exit 1
fi

Recommended Layout

 .
 └─ src
     ├─ main
     │   └─ bash
     │       └─lib
     │          ├─ mylib1.shlib
     │          └─ mylib2.shlib
     └─ test
         └─ bash
             │ 
             ├─ mylib1
             │    ├─ libraries.bash
             │    ├─ data
             │    │   └─ .gitkeep
             │    ├─ myfunction1.bats
             │    └─ myfunction2.bats
             │ 
             └─ mylib2
                  ├─ libraries.bash 
                  ├─ data
                  │   └─ .gitkeep
                  ├─ myfunction3.bats
                  └─ myfunction4.bats

  • Each library to be tested has its own directory.
  • Each library-specific directory contains a libraries.bash that lists dependencies, including the library to be tested. For more details on how to compose that file, see Loading a Library to be Tested into a .bats Test below.
  • Each function to be tested has its own .bats file, where various scenarios to test the function are implemented as individual @test functions.
  • The library's directory has a data subdirectory in which a data/tmp will be created dynamically as needed. For more details see Handling data section below.

Loading a Library to be Tested into a .bats Test

Assuming that the library you want to test is called blue.shlib, create a blue-library.bash file in the directory that contain the .bats tests for the library. If the library is located at a fixed relative location to the .bats test files, use this arrangement:

source "${BATS_TEST_DIRNAME}/[...]/blue.shlib"

Otherwise, you can use an absolute path, but that is more fragile.

From each .bats test that wants to test functionality from that library:

load blue-library 

@test "something" {
...
}

If more than one library needs to be loaded, create a libraries.bash file that sources all libraries, including the one that needs to be tested:

source "${BATS_TEST_DIRNAME}/[...]/lib1.shlib"
source "${BATS_TEST_DIRNAME}/[...]/lib2.shlib"
source "${BATS_TEST_DIRNAME}/[...]/lib3.shlib"
source "${BATS_TEST_DIRNAME}/[...]/blue.shlib"

and from each BATS test, load it with:

load libraries
@test "something" {
...
}

For more details, see:

Libraries being Tested and Helpers

Loading an Executable Script as Library to be Tested into a .bats Test

It is possible to test functions from an executable script by loading the executable script as library, even if the script loads external libraries itself, and invokes a main function. We use the same idea described above in the Loading a Library to be Tested into a .bats Test section, with a few additions. Let's assume there is a script called blue that is executable (contains a main() invocation), sources the bash.shlib library, and contains functions that we would like to unit test:

#!/usr/bin/env bash

#
# this is blue
#
[ -f $(dirname $0)/lib/bash.shlib ] && source $(dirname $0)/lib/bash.shlib || { echo "[error]: $(dirname $0)/lib/bash.shlib not found" 1>&2; exit 1; }

function main() {
...
}

function auxiliary() {
...
}

main "$@"

We would like to test auxiliary() by loading blue as a library.

For that, the following changes need to be introduced in blue:

1. Guard library loading with a BATS_TESTING variable:

...
[ -z "${BATS_TESTING}" ]  && { [ -f $(dirname $0)/lib/bash.shlib ] && source $(dirname $0)/lib/bash.shlib || { echo "[error]: $(dirname $0)/lib/bash.shlib not found" 1>&2; exit 1; } }
...


2. Guard main() invocation with the same BATS_TESTING variable:

...
if [ -z "${BATS_TESTING}" ]; then
    main "$@"
fi
...

Interestingly enough, this does not work, it breaks BATS:

...
[ -z "${BATS_TESTING}" ] && main "$@"
...

3. Create the libraries.bash library loader in the directory that contains the .bats tests. The library loader is similar to the one described in the section Loading a Library to be Tested into a .bats Test above. The difference is that it must set the BATS_TESTING to true, load the executable script as library, and also load all libraries required by the executable script:

export BATS_TESTING=true
source ${BATS_TEST_DIRNAME}/../../lib/bash.shlib
source ${BATS_TEST_DIRNAME}/../../blue

4. For each .bats test that wants to test functionality from the executable script, load libraries, similarly to how is described in Loading a Library to be Tested into a .bats Test:

load libraries

@test "something" {
...
}

Handling stdout and stderr

function teardown() {

    rm -f ${BATS_TEST_DIRNAME}/tmp/stdout
    rm -f ${BATS_TEST_DIRNAME}/tmp/stderr
}

@test "stderr" {

    run $(fail "blah" 1>${BATS_TEST_DIRNAME}/tmp/stdout 2>${BATS_TEST_DIRNAME}/tmp/stderr )

    [[ -z $(cat ${BATS_TEST_DIRNAME}/tmp/stdout) ]]
    [[ $(cat ${BATS_TEST_DIRNAME}/tmp/stderr) = "[failure]: blah" ]]
}


This is a proposed solution, I need to do more research:

@test "test all outputl" {
  local stdoutPath="${BATS_TMPDIR}/${BATS_TEST_NAME}.stdout"
  local stderrPath="${BATS_TMPDIR}/${BATS_TEST_NAME}.stderr"

  myCommandOrFunction 1>${stdoutPath} 2>${stderrPath}

  grep "What Im looking for in stdout"  ${stdoutPath}
  ! grep "something it cannot appear in stdout"  ${stdoutPath}

  grep "What I'm looking for in stderr" ${stderrPath}
  ! grep "Something it cannot appear in stderr" ${stderrPath}
}

Handling stdin (Testing Code that Reads stdin)

A tested pattern that works when stdin is pulled from with read is to "override" read as such:

@test "some test" {

    # bash read "override"
    function read() {

        debug "read($@)"
        local variable_name=$3 # this is the index of the argument that specifies 
                               # the variable name. It is valid for "read -p 'something' some_var".
        eval "${variable_name}=\"black\""
    }

    run some_function

    ...
}

This behaves as "black" was provided at stdin.

Handling data

A useful pattern is to create a data subdirectory in the directory that holds the .bats tests, and then access it from the tests with ${BATS_TEST_DIRNAME}/data/....

Data and Temporary Directories

function setup() {
  [[ -d "${BATS_TEST_DIRNAME}/data/tmp" ]] || mkdir -p "${BATS_TEST_DIRNAME}/data/tmp"
}

function teardown() {
  rm -rf "${BATS_TEST_DIRNAME}/data/tmp"
}

"Override" Functions

A useful technique when testing a layer is to "override" (declare) the functions called from that layer right before the test, so we can control input and output in the layer.

function setup() {

    function some-function() {
        echo "some-function($@)"
    }
}

...

@test "test invocation through layer to some-function()" {

    run caller-function A B C

    [[ ${status} -eq 0 ]]
    [[ "${output}" =~ some-function\(.*\) ]]

    args=${output#some-function(}
    args=${args%)}

    arg0=${args%% *}
    [[ ${arg0} = "A" ]]

    args=${args#${arg0} }
    arg1=${args%% *}
    [[ ${arg1} = "B" ]]

    ...
}

The function does not need to be "overridden" in setup(), they can be overridden in the test itself:

@test "test invocation through layer to some-function()" {

    function some-function-that-is-invoked-inside-caller-function() {
       exit 1
    }

    run caller-function A B C

    [[ ${status} -eq 1 ]]

    # ...
}

A default, innocuous override can be specified in setup() - so the majority of the tests that rely on the correct behavior get that - while tests that are involving with specific error conditions can "override" the function locally and test a specific error condition.

Moreover, the "overridden" function can test its arguments and cause the test to fail on invalid arguments:

@test "test invocation through layer to some-function()" {

    function some-function-that-is-invoked-inside-caller-function() {
       local arg1=$1
       [[ ${arg1} = "some expected value" ]] # this will fail the test if the arg1 value is not the one that is expected
    }

    run caller-function A B C

    [[ ${status} -eq 0 ]]

    # ...
}

If tests specified in more than one .bats files need to share the same function behavior, the function can be declared in its own .shlib file in the same directory as the bats files, and then it can be sourced in the setup() or individual tests:

mock-curl.shlib:

function curl() {
 ...
}

In the test's setup() function:

function() setup() {
   ...
   source ${BATS_TEST_DIRNAME}/mock-curl.shlib
}

Debugging Function Invocations

function setup() {
    [[ -d ${BATS_TEST_DIRNAME}/data/tmp ]] || mkdir "${BATS_TEST_DIRNAME}"/data/tmp
    INVOCATION_FILE=${BATS_TEST_DIRNAME}/data/tmp/invocations.txt
    function some-function() {
      echo "$@" >> "${INVOCATION_FILE}"
      ...
    }
}

function teardown() {
  rm -rf "${BATS_TEST_DIRNAME}"/data/tmp
}

@test "some test" {
  run ...
  ...
  # shellcheck disable=SC2002
  invocation_count=$(cat "${INVOCATION_FILE}" | wc -l | awk '{print $1}')
  [[ ${invocation_count} == 2 ]]
  first_invocation=$(head -1 "${INVOCATION_FILE}")
  [[ ${first_invocation} == "something" ]]
  second_invocation=$(head -2 "${INVOCATION_FILE}" | tail -1)
  [[ ${second_invocation} == "something else" ]]
}

Running Individual Tests

Individual tests from a .bats test file can be executed by using the -f, --filter option, with a regular expression that matches the test(s) names.

bats ./some-file.bats -f "blue test"

An empty filter acts as no filter, all tests will be executed:

bats ./some-file.bats -f ""

Debugging Failures

BATS does not handle error logging gracefully. A useful technique is to turn verbosity in bash.shlib debug()/error()/warn()/info() functions and to configure them to send output into an alternate file, for problematic tests:

VERBOSE=true; DEBUG_OUTPUT=~/bats.out

Run the tests as follows:

rm ~/tmp/bats.out ; clear; bats ...test/some-test.bats; cat ~/tmp/bats.out