Linux

Bash scripting – loops, string concatenation and passing arguments

I’ve just created some very basic bash script to automate my task, that is to run behat tests for several test directories. It involves passing argument from command line, doing loops on arrays and concatenating strings. See below for more details.

UPDATE: I’ve just updated the post after discovering that the script can be further enhanced by using bash functions. See updated script at the bottom.

The project

The project is not that large, however, there are some 5 or 6 sites on it with multiple environments like development environments for multiple developers, staging and production. I need to run tests for all sites at specific environment. I’m using behat as tool for running automated tets (BDD). Below is the test directory structure.

|-- test_dir_root
    |-- site1
    |   |-- behat.yml
    |   `-- features
    |-- site2
    |   |-- behat.yml
    |   `-- features
    |-- site3
    |   |-- behat.yml
    |   `-- features
    |-- site4
    |   |-- behat.yml
    |   `-- features
    |-- site5
    |   |-- behat.yml
    |   `-- features
    `-- site6
    |   |-- behat.yml
    |   `-- features
    `-- run_tests.sh

Strategy

The strategy is simple, go to test’s root directory, loop through all directories (listed in an array) then run the tests with uniform parameters, ex: if I want to run a staging test, it should run tests against staging environment for all sites listed.

Since behat‘s default behavior is to run in verbose mode, I have to pass some parameters to have a less cluttered test output.

The Script

I’ve put script at the root directory for the tests and name it run_tests.sh. Below is the code.

#!/bin/sh

if [ "$1" == "" ]; then
    echo "Usage: ./run_tests.sh <production|staging|development>" 
    echo "Ex: ./run_tests.sh staging" 
    exit 1
fi

TEST_ROOT_DIR=$(pwd)
SITES=("site1" "site2" "site3" "site4" "site5" "site6")

echo "Initiating tests..."

for i in "${SITES[@]}"
do
    :
    # Go to test root dir
    cd "$TEST_ROOT_DIR"
    TARGET_DIR="$TEST_ROOT_DIR/$i"
    COMMAND="behat --profile $1 --format progress"
    cd "$TARGET_DIR"
    echo "Running tests for $i at $(pwd)"
    $COMMAND
    echo ""
done

As you can see, it has some basic control structure like if statement, loops and even string concatenation. The code block:

if [ "$1" == "" ]; then

checks if the first command line argument is empty. If you are using two command line arguments, it will be at $2, third will be at $3 and so on. The code block:

SITES=("site1" "site2" "site3" "site4" "site5" "site6")

declares an array. The array is then used for looping. The code block:

TARGET_DIR="$TEST_ROOT_DIR/$i"

concatenates the string variable $TEST_ROOT_DIR and $i with / at the middle.

Let’s run it. Assuming they all passes, below will be how it will look like.

lysender@darkstar-lenovo-32:/home/data/www/huge_project/behat$ ./run_tests.sh darkstar
Initiating tests...
Running tests for site1 at /home/data/www/huge_project/behat/site1
....................

2 scenarios (2 passed)
20 steps (20 passed)
0m0.211s

Running tests for site3 at /home/data/www/huge_project/behat/site3
..............................................................

18 scenarios (18 passed)
62 steps (62 passed)
0m1.238s

Running tests for site3 at /home/data/www/huge_project/behat/site3
....................

2 scenarios (2 passed)
20 steps (20 passed)
0m0.214s

Running tests for site4 at /home/data/www/huge_project/behat/site4
.........F.........F

(::) failed steps (::)

01. ...

02. ...

2 scenarios (2 failed)
20 steps (18 passed, 2 failed)
0m0.226s

Running tests for site5 at /home/data/www/huge_project/behat/site5
.........F.........F

(::) failed steps (::)

01. ...
02. ...

2 scenarios (2 failed)
20 steps (18 passed, 2 failed)
0m0.218s

Running tests for site6 at /home/data/www/huge_project/behat/site6
....................

2 scenarios (2 passed)
20 steps (20 passed)
0m0.214s

lysender@darkstar-lenovo-32:/home/data/www/huge_project/behat$ 

That’s it. Enjoy and share.

Update!

After being annoyed by running all tests for all sites every single time, I decided to add an option to I could just run a test for just one site. Therefore, it will either run tests for all sites or run test for just one site. Below is the updated script.

#!/bin/sh

if [ "$1" == "" ]; then
    echo "Usage: ./run_tests.sh <production|staging|development> <optional site_name>" 
    echo "Ex: ./run_tests.sh staging" 
    echo "Ex: ./run_tests.sh staging site2" 
    exit 1
fi

TARGET_SITE=""
if [ "$2" == "" ]; then
    SITES=("site1" "site2" "site3" "site4" "site5" "site6")
else
    SITES=("$2")
fi

BEHAT_PROFILE="$1"
TEST_ROOT_DIR=$(pwd)

run_behat_test_on_target() {
    # $1 is root dir
    # $2 is the target site
    # $3 is the profile name
    TARGET_DIR="$1/$2"
    COMMAND="behat --profile $3 --format progress"
    cd "$TARGET_DIR"
    echo "Running tests for $2 at $(pwd)"
    $COMMAND
    echo ""
}

echo "Initiating tests..."

for i in "${SITES[@]}"
do
    :
    run_behat_test_on_target $TEST_ROOT_DIR $i $BEHAT_PROFILE
done

To run the script, we doe something like below:

# Run tests for just site 2
./run_tests.sh staging site2
# Run tests for all sites
./run_tets.sh staging

Leave a reply

Your email address will not be published. Required fields are marked *