Android developers: start writing scripts now!

Audric Steibel
9 min readJan 31, 2021
Android developers: start writing scripts now!

As a mobile developer, I spend a lot of time writing code, but also a lot of time testing the code I’ve just written. It means there are times that a lot of repetitive tasks need to be performed, in order to test particular scenarios. One way to make this process faster and more productive is to write scripts, that can run a series of commands and automate certain tasks.

In this article, I’ll explain how I wrote one script that can take an input, and run some API queries to automate a task. This is written from the perspective of someone who rarely writes scripts, so I’ll go over some of the very basic things you need to know to write your first script:

  • reading input
  • querying APIs
  • parsing their responses
  • creating functions
  • if and loop statements, etc…

Some context

Here’s something I was working on recently: adding into an existing app a kind of test, in which the user answers 25 questions and gets a score at the end. The implementation was pretty straightforward, but when it came to testing what I had developed, I quickly realized I was going to have a problem: whenever I wanted to test with profiles that had done the test, and had either passed it or failed it, I had to create a new account and then answer all 25 questions. Obviously, that’s a huge waste of time, and I thought there should be an easier way.

I decided I would write a short bash script that would automate this process. I had never really done something like this before, I rarely write bash scripts myself, so it was interesting for me to try something new.

Here’s what I needed the script to do:

  • Identify a user by giving in the input a member id.
  • With that member, start a new test
  • For each question of the test, submit an answer
  • I want to try different cases: a complete success (100%), a partial success (say 80%), a partial failure (say 20%), and a total failure (0%).

The code

I’ll go over every individual part of the script, and you’ll find at the end of the article the complete script.

Reading an input

Let’s start with getting the input from the command terminal. The command to read what is entered in the terminal is, well, read, which is then assigned to a variable. To make it look nice (and also, you know, make the script user understand what’s happening), I’ll need to preface it with some text, using the command echo.

You can also use echo to verify that this works as expected, by printing the variable after input. To use a variable, use $ before the name of the variable.

Put together, this first part is:

echo "Enter member id:"
read MEMBER_ID
echo "The member id is $MEMBER_ID"

Let’s run this first part. To do so, save the file with the .sh extension, for example, test_script.sh.

Open a command terminal. You’ll need to run chmod +x test_script.sh, to change the permission of the file and make it executable. Next, simply run your script.

...audric$ chmod +x test_script.sh
...audric$ ./test_script.sh
Enter member id:
1
The member id is 1

Ok, our first input is stored.

For the second one, which is choosing what kind of outcome I want from the test, I decided to enter the 4 different outcomes, and simply ask the user to enter a number between 1 and 4 to choose between them. I store this information in a MODE variable.

echo "Choose a test result:
1 -> Success, 100% score.
2 -> Success, but not 100% score.
3 -> Failure, 0% score.
4 -> Failure, mix of passed and failed categories."
read MODE

Validating the input

At this point, I decided to do a validation of the input, to assure myself that the number entered was between 1 and 4. This led me to my first if condition in my script. The structure of an if is the following:

if [[ condition ]]; then
#statements
fi

In case you wanted to add multiple ifs, here’s the full structure:

if [[ condition ]]; then
#statements
elif [[ condition ]]; then
#statements
else
#statements
fi

In my case, I just wanted to test a single thing: that the entered number was between 1 and4. To check this, I used a regular expression, testing against those 4 numbers. In case the number entered was not one of them, I print an error message and exit the script.

if [[ ! $MODE =~ ^[1-4]+$ ]] ; then
echo "Incorrect input."
exit
fi
echo "Chosen $MODE."

And here are some examples:

...audric$ ./test_script.sh
Choose a test result:
1 -> Success, 100% score.
2 -> Success, but not 100% score.
3 -> Failure, 0% score.
4 -> Failure, mix of passed and failed categories.
2
Chosen 2.
...audric$ ./test_script.sh
Choose a test result:
1 -> Success, 100% score.
2 -> Success, but not 100% score.
3 -> Failure, 0% score.
4 -> Failure, mix of passed and failed categories.
5
Incorrect input.

Making and parsing API requests

The next thing to do on my list was to create a test. To do so, I needed to interact with my REST API. I have to do a simple POST request to an endpoint and add in the request body a json object containing the member id, as well as some other information, that can be hardcoded in for this script as it is not subject to change.

You might know this already, but the command to execute HTTP requests in command line is curl.

You can use the -X option to specify the method (here: POST). I’m also going to send a json object in my request, so I’ll use the -d option to add this data, and the -H header for adding headers. To keep things simple, I’ll only show a streamlined version of my json body, and only the headers for the json formatting. As always, use curl — help to learn more about all the options available to you.

curl -X "POST" "https://my.api.com/test" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d $'{
"member_id": '$MEMBER_ID'
}'

This returns a fairly big json object, but the only thing that interests me now is getting the ATTEMPT_ID from this json object, which I’ll then use for sending the answers to the questions of my test during this attempt.

To parse the json and get this ID, I’ve used a very efficient library called jq. Head over to its documentation to see how to install it and to have more details on how to use it.

For our needs, I now simply need to run this to capture the id from my json object:

jq -r '.id'

Of course, if your json object is more complex than the ID being the first element of the object, then you will need to write a more complex query. Check the jq documentation to learn the query matching your needs.

Here, I want to run this command on the result of my previous curl and save this result in a new variable, ATTEMPT_ID.

ATTEMPT_ID=$(curl -X "POST" "https://my.api.com/test" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d $'{
"member_id": '$MEMBER_ID'
}' | jq -r '.id')

Let’s break this down quickly:

The | operator allows us to use the output of the first command as an input in the second command. This is how jq will know which json object to use.

Then, I’m simply saving the result of this double operation in a new variable, ATTEMPT_ID.

I can now move forward to making requests to fill my questionnaire.

Using functions

To answer a question in the test, I need to do a POST request with an attempt ID and a question ID, containing the selected answer ID in a json object. This is not too different than the request we just saw before. However, we’re not going to manually write a new request for all 25 questions, when the only variations are the question and answer IDs.

Instead, we’ll write this new request in a function, and we’ll call it instead for each question/answer pair.

You can declare functions are the top of your script, using this structure:

my_function()
{
content of the function...
}

And to later call your function during your script, you’ll do:

my_function param_1 param_2 ...

In our example, we’ll have 2 parameters, the question ID and answer ID.

The first thing to do in our function’s body is to get those 2 parameters from the input.

To do so, declare a new variable in the body of your function, and use $1 and $2 to get the values provided to it.

Here’s my full request:

send_request() 
{
QUESTION_ID=$1
SELECTED_ANSWER_ID=$2
curl -s -X "POST" "https://my.api.com/$ATTEMPT_ID/question/$QUESTION_ID" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d $'{
"selected_answers_ids": [
'$SELECTED_ANSWER_ID'
]
}' > /dev/null
echo "Question $QUESTION_ID answered"
}

You can see it’s simply assembling all that we’ve viewed so far, with a few additions:

  • I used -s and > /dev/null in my query in order to run it silently, and avoid spamming the user’s log.
  • I then ran an echo command in order to inform the user of each question being answered, to visualize the progress of the script as it is running.

Now that we have our function, let’s call it as such:

send_request 1 1

Here, I’ll answer the 1st question with the answer ID 1.

Adding a for loop

The final thing we need to do is to answer all 25 questions, for each of the 4 cases.

As we’re not going to write 25*4 = 100 lines of code to answer all the questions, we’ll create a for loop and answer the questions more efficiently.

For this, I’m first going to create a list of answers for each of the 4 cases, selecting the IDs of the question matching the outcome I expect for that mode.

if [[ $MODE -eq 1 ]]; then
answers=(4 6 12 ...)
elif [[ $MODE -eq 2 ]]; then
answers=(4 6 13 ...)
elif [[ $MODE -eq 3 ]]; then
answers=(1 5 10 ...)
elif [[ $MODE -eq 4 ]]; then
answers=(1 5 10 ...)
fi

Now that I have my answers, I’ll do a loop.

The format of a for loop is as follow:

for (( i = 0; i < 10; i++ )); do
#statements
done

In my case, I have 25 questions to go through, so here it is:

for (( i = 0; i < 25; i++ )); do
send_request $((i+1)) ${answers[$i]}
done

The question IDs start at 1, hence the +1 for the first parameter of the function. I first send the ID of the question, and then the ID of the answer.

And voilà!

We can finally put everything together and run our script, and it should automatically answer all 25 questions for us in a fast and efficient way.

Further improvements

Here are some ideas of how to improve this script, and what other types of scripts we could think of:

First, this example was chosen for its simplicity. Obviously, in a production-ready product, you won’t send the member ID in a json body like this but instead will probably have some kind of authentication. A way to include this would be to do the same as we did for the attempt ID: ask the user to enter the information needed to identify them, run the auth request, and capture whichever token you need from its return. You can then use this token in your following requests.

Another improvement I thought of is to finishing answering a test partially answered. Maybe the user we’re testing with has started it in the app, but we want to speed up the process via a script. For this, assuming the API can handle this, we could query for the ongoing test, retrieve its Id (instead of creating a new one), and only answering the unanswered question. I’d envision this as a separate script though, let’s keep them all simple for now.

Finally, an idea of a similarly useful script that we could work on, would be one to create new users and fill in their profile. Something I have to do a lot as I develop my app is creating loads of users in the staging environment, to test various things (UX of new users, UX of completing a profile, testing with users with different kinds of profile, etc…). The same thing goes for the QA team of course. Therefore, a script that automatically creates a new user for you, and automatically fills the profile in the way you want (or completes an onboarding, or completes a registration flow…) would be great. Maybe I’ll write a follow-up post if I do end up writing it!

Final thoughts

I wrote this script in order to make my development process easier and more efficient, but it also has the bonus effect of greatly helping everyone else that needs to interact with that specific feature, such as my QA team, or the API developers. So adding bash scripts to your workflow can be something very helpful for a number of people in your team.

I hope you found this article useful, and that it will inspire you to start writing scripts as well, to work more efficiently, and save a lot of time not doing the same repetitive tasks over and over again!

--

--

Audric Steibel

Product Manager, previously Mobile Developer. Interested in building great products and providing users with a fantastic user experience.