Search code examples
bats-core

Is there a way to bail from a bats test?


Is there a way to bail out of an entire test file? the entire test suite?

Something along the lines of

@test 'dependent pgm unzip' {
  command -v unzip || BAIL 'missing dependency unzip, bailing out'
}

Edit:

I can do something like

#!/usr/bin/env bats

if [[ -z "$(type -t unzip)" ]]; then
  echo "Missing dep unzip"
  exit 1
fi

@test ...

And that works fine for checks to be run at the beginning of a test, except that it doesn't output as part of the report.

But if I want to determine if a sourced script correctly defined a function and bail if it's not, adding that kind of test prevents any kind of a report being generated. Successful tests are not displayed.


Solution

  • TL;DR

    • To see the abort message, redirect the message in global scope to stderr using >&2.
    • To abort all files after failure, use exit 1 in global scope.
    • To only abort a single file, create a setup function that uses skip to only abort the tests in that file.
    • To fail tests in a single file, create a setup function that uses return 1 to fail the tests in that file.

    The answer in more detail

    Aborting all files

    You were almost there with your second example. The trick is to redirect the output to stderr1.

    Using exit or return 1 from the global scope will halt the entire test suite.

    #!/usr/bin/env bats
    
    if [[ -z "$(type -t unzip)" ]]; then
      echo "Missing dep unzip" >&2
      return 1
    fi
    
    @test ...
    

    The drawback is that any tests in and after the aborted file will not be run, even if those tests were to pass.

    Aborting a single file

    A more fine-grained solution would be to add a setup2 function that will skip3 if the dependency is not present.

    As the setup function is called before each test in the file it is defined in, all tests in that file will be skipped if the dependency is missing.

    #!/usr/bin/env bats
    
    setup(){
        if [[ -z "$(type -t unzip)" ]]; then
            skip "Missing dep unzip"
        fi
    }
    
    @test ...
    

    Failing rather than skipping

    It is also possible to fail tests that have a unmet dependecy. Using return 1 from the a test's setup function will fail all tests in that file:

    #!/usr/bin/env bats
    
    setup(){
        if [[ -z "$(type -t unzip)" ]]; then
            echo "Missing dep unzip"
            return 1
        fi
    }
    
    @test ...
    

    As the message output is not in global scope, it does not have to be redirected to sdterr (although that will work too).

    Footnotes

    1. There is mention of this at the bottom of the page about Bats-Evaluation-Process in the wiki and in the manual (if you run man 7 bats):

       CODE OUTSIDE OF TEST CASES
      
           You can include code in your test file outside of @test functions.
           For example, this may be  useful  if  you  want  to check for
           dependencies and fail immediately if they´re not present. However,
           any output that you print in code outside of @test, setup or teardown
           functions must be redirected to stderr (>&2). Otherwise, the output
           may cause Bats to fail by polluting the TAP stream on stdout.
      
    2. For details on setup see https://github.com/bats-core/bats-core#setup-and-teardown-pre--and-post-test-hooks

    3. For details on skip see https://github.com/bats-core/bats-core#skip-easily-skip-tests