Search code examples
bats-core

BATS - How to force a check for existence of a program to fail?


I have a function that checks for both the existence of a file and if it's executable.

is_command() {
  [[ -x "$(command -v "$1" 2> /dev/null)" ]] && return 0
  return 1
}

I have a script that is requiring dos2unix.

is_command 'dos2unix' || { echo 'dos2unix is required'; exit 1}

Since dos2unix is installed in /usr/bin I can't just remove the installed path from PATH. I also don't want to (and can't anyway) temporarily rename the actual dos2unix program.

I tried setting an entry in BASH_CMDS, and it works from the command line:

$ hash -r ; BASH_CMDS['dos2unix']='/no/such/dir/dos2unix' ; [[ -x "$(command -v dos2unix 2> /dev/null)" ]] ; echo $?
1

But this test is not working. The assert_output does not match (it's usage message from later in the program).

 @test 'dos2unix does not exist' {
   hash -r
   BASH_CMDS['dos2unix']='/no/such/dir/dos2unix'
   note "dos2unix: $(command -v dos2unix)"
   run ds
   assert_failure
   assert_output 'dos2unix is required'
 }

How can I make this work? and why isn't this working?

Edit: Make the assert_output match the error generated.


Solution

  • Lets say your file-under-test looks like this:

    #!/usr/bin/env bash
    
    ds() {
    
        is_command() {
          [[ -x "$("${command}" -v "$1" 2> /dev/null)" ]] && return 0
          return 1
        }
    
        is_command 'dos2unix' || { echo 'dos2unix is required'; exit 1; }
    
        exit 0;
    }
    

    You would have to somehow "load" this code into your test before it can be run.

    Most often, the BATS's load function is used for this. However, this (or a similar setup) messes with the scope of BASH_CMDS.

    This can be resolved by sourcing the file from the global scope:

    #!/usr/bin/env bats
    
    source "${BATS_TEST_DIRNAME}/ds.sh" # <-- This part is important
    
    @test 'dos2unix does not exist' {
        hash -r
    
        BASH_CMDS['dos2unix']='/no/such/dir/dos2unix'
    
        run ds
    
       assert_failure
       assert_output 'dos2unix is required'
    }
    

    When I run your original code, it indeed fails. With source, the test passes for me.

    If this does not resolve your issue, other things might (also) be going on...