Your next acceptance tests framework… bash ?

I have been trying several frameworks to test my pieces of software. The choice was difficult to make between cucumber-js, cucumber-java or karate.

All of them need a complex runtime. Either by installing a node_modules packages directory with a lot of dependencies, or by installing them in a .m2/repository directory.

I was wondering myself : Is it difficult to write a test that needs just an OS to run as pre-requisite ? I know that shell is a very powerful language, so I allowed myself to push the idea of testing my service only which a bash script. (at the end my team decided to use karate instead, but I think the idea is still worth the trial)

#!/usr/bin/env bash

HEALTH_COMMAND="curl $API_URL_PREFIX/health--connect-timeout 1 -s -o /dev/null"

function ensureJavaVersionIsOk(){
local JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)
echo " expected java >= $MINIMUM_JAVA_VERSION but got $JAVA_VERSION"
echo " java version : $JAVA_VERSION"

function startService (){
local stepsize=1
local frames='⠁⠂⠄⡀⢀⠠⠐⠈'

if [ $(${HEALTH_COMMAND} ; echo $?) -eq 0 ];then
echo "$1 appears to be started already, and I cannot control the process" >&2
local jarfile=$(ls -S ../$1/target/ | head -n 1)
java -jar ../$1/target/${jarfile} >/dev/null 2>/dev/null &
local API=$(echo $!)

while [ $(${HEALTH_COMMAND} ; echo $?) -gt 0 ] && [ ! -z "$(ps | grep ${API})" ];do
s=$(( (s+$stepsize) %${#frames} ));
echo -ne "\r${frames:$s:$stepsize} starting $1" >&2

if [ $(${HEALTH_COMMAND} ; echo $?) -gt 0 ];then
echo ""
echo "💥 $SERVICE_NAME could not start"

echo -e "\r$1 started (process id is $API)" >&2

function killService(){
if [ ! -z "$2" ];then
echo ""
echo "👍 killing service $1"
kill -9 $2

function success(){
killService ${SERVICE_NAME} ${PID}
echo ""
echo " Ok, thanks, bye"
exit 0

function failure(){
killService ${SERVICE_NAME} ${PID}
echo ""
echo " tests failed"
exit 1

function downloadJq(){
if [ ! -z "$(which jq)" ]; then
echo " jq is already installed"
elif [ ! -z "$(cat /proc/version | grep MINGW64)" ]; then
echo "→ downloading jq for windows"
curl $(curl -v 2>&1 | grep Location: | cut -d' ' -f3) -o jq.exe -s
elif [ ! -z "$(cat /proc/version | grep Linux)" ]; then
echo "→ downloading jq locally in this env"
curl $(curl -v 2>&1 | grep Location: | cut -d' ' -f3) -o jq -s
chmod a+x ./jq
export PATH=PATH:$(pwd)

function title(){
echo ""
echo -e "\033[1m $1 \033[0m"

function findValue(){
echo "$1" | jq -r $2

function assert() {
# First parameter is the message in case the assertion is not verified

# The remaining arguments make the command to execute

# Run the command, $@ ensures arguments will remain in the same position.
# "$@" is equivalent to "$1" "$2" "$3" etc.
RESULT=$(eval "$@")

# Get the return code

# If everything is okay, there's nothing left to do
if [ ${rc} -eq 0 -a "$RESULT" != "null" ]; then
echo "⚛️ ${message}"
return 0

# An error occured, retrieved the line and the name of the script where
# it happend
set $(caller)

# Get the date and time at which the assertion occured
date=$(date "+%Y-%m-%d %T%z")

# Output an error message on the standard error
# Format: date script [pid]: message (linenumber, return code)
echo " assertion failed : $message (line $1)" >&2

# Exit with the return code of the assertion test

function getAgentToken(){
curl -s -X'POST' -E myfile.pem:password -d'user=1&group=2&agent=autoadmin' | jq -r '.token'

function getToken1(){
curl -s -X'POST' -E myfile.pem:password -d'username=johndoe&password=password' | jq -r '.token'

function getToken2(){
curl -s -X'POST' -E myfile.pem:password -d 'username=johnsmith&password=password' | jq -r '.token'

function callApi(){
echo "curl -s -H'Content-Type: application/json; charset=utf-8' -H 'Authorization: Bearer $1' $API_URL_PREFIX/$2 -d '$3'"

function firstTest(){
echo "$(eval "$(callApi ${TOKEN_1} test1 '{"data":"'$1'"}')")"

function secondTest(){
echo "$(eval "$(callApi ${TOKEN_2} test2 '{"data":"'$1'"}')")"

startService ${SERVICE_NAME}

title "Test first api"
DATA_1=$(firstTest myValue)
assert "first api response should have at least one item with an idea" [ ! -z "$(echo ${DATA_1} | jq ".[].id")" ]
assert "first item price should be 32 dollars" [ \"$(findValue "${DATA_1}" ".[].price_details.currency") $(findValue ${RELATED_PRODUCT_ITEM} ".[].price_details.total_price")\" = \"USD 32\" ]
DATA_1_ID="$(echo ${DATA_1} | jq -r ".[].id")"

title "Test second api"
DATA_2=$(secondTest ${DATA_1_ID})
assert "there is some data named JOHN" "[[ ! -z \"\$(echo \${DATA_2} | jq -r \".data[] | select(.name==\\\"JOHN\\\")\")\" ]]"
assert "there is some data named SMITH" "[[ ! -z \"\$(echo \${DATA_2} | jq -r \".data[] | select(.name==\\\"SMITH\\\")\")\" ]]"

It is possible to nest that execution during a maven / gradle build, just use a naive plugin like exec-maven-plugin (call your file for example)


There you are. A shell program that will start your api, execute some tests and run some assertions. It uses unix as dependency. This is lightweight and very focused. Have fun with it.

Leave a Reply

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


This site uses Akismet to reduce spam. Learn how your comment data is processed.