Jul 12, 2021 by Mihai Balica
Automate everything! (Part 2)
A story about automation and how we can use different technologies together to reach a common goal
Cognizant Softvision QC Technical Coordinator, Mihai Balica shared a case study with a high level overview of an automation solution in the first part of this article. In this second half of “Automate everything!” Mihai covers the implementation details with some source code samples.
The Proof of Concept – Implementation
Arduino programming – code structure
Generally, each Arduino code has a part where variables are declared (in our case, the servo motors and the bluetooth device), followed by a setup() function (where we initialize variables, pin modes, etc.) and then the loop() function, where the program loops endlessly, doing what we tell it to do. The setup() function will execute only once at power-up or reset of the Arduino board.
Declaration, Setup and Loop
See Appendix 1.
See source code in Appendix 2.
We initialize the servos by attaching each servo motor to a specific PIN number on the Arduino board. Since we are not using the most expensive hardware, we must set a lower level (e.g. 9600) for the bluetooth communication baud rate. And the truth is, we don’t actually need more than that. The int variables represent different angles for positioning the robotic arm. Each servo must have a specific movement position, in degrees, but we also need to store the previous position and the speed we want for the servo movement. For instance, when entering one digit of the PIN number, we need to know the initial position of all servos, then we rotate the servos in such a way that the robotic arm will touch the screen at a specific position, with a certain speed and then it will move back to its initial positions.
Loop it forever
As you might have guessed, the program loops through this function and executes its code. At each loop we are listening to the bluetooth device for a string to be sent to the Arduino board. That string is interpreted as a command given to the “robocop” – the name given to the Arduino board and all the hardware controlled by it.
If the string is “t” it is interpreted as a command to “TAP the card.” This translates to a movement of the servo motor number 5 to a specific angle. The card attached to this servo touches the terminal, waits for 10 seconds and then moves back to the initial position. For entering the PIN number for instance, it requires a movement of 3 servo motors, so the code is a little bit more complicated, but the principles are the same.
At this point, the Arduino board is waiting for a 1 character string to arrive on the serial communication (the bluetooth device) at each loop in order to execute the proper piece of code. The coding of commands are pretty simple:
- “t” for TAP action
- “i” for INSERT CARD action
- “o” for extracting CARD
- “e” for Press ENTER on screen
- “0” to “9” for pressing the PIN number on screen
- “s1” to “s5” followed by a 3-digit number for moving a specific servo to the indicated angle (ex. s1120 to move servo1 to 120 degrees). These were used for calibrating the robotic arm in the early stages of development, but are not used in the actual setup.
See Appendix 3.
Android test framework
Words of wisdom
“Automation can be defined as the technology by which a process or procedure is performed without human assistance” – Groover Mikell (2014)
How to control the robotic arm from our test framework
The automation framework is written in Java, and everything related to this robotic arm is encapsulated in a Class named Bluetooth.java. Since the framework is following the Page Object Model, we also have a class, AppBase.java, where we perform all the common tasks from the test cases, like initializing the Bluetooth object:
In order to allow the execution of automated tests without the robotic arm, a switch was needed: BLUETOOTH_PRESENT of type String, is set as environment variable when launching the tests:
The code above initializes the test-case with the presence of the bluetooth device set to false by default if not specified to be true.
By default, BLUETOOTH_PRESENT is set to false. That means the Android device’s bluetooth will not connect to the Robotic arm’s bluetooth in order to send any commands. BLUETOOTH_PRESENT value will be sent in the constructor of the Bluetooth.java class.
Because of the inexpensive hardware we are using, a lot of interference may occur with other wireless devices (laptops, smartphones, smartwatches, etc.) that results in our bluetooth connection failure. For this reason, in the constructor code below, if the bluetooth presence is true (active variable of type String), it tries connecting to the robotic arm’s bluetooth device up to 10 times.
See Appendix 4
Setting the robotic arm as active or inactive will determine the way the test framework behaves. We use the private method setActive(boolean) for this in the constructor. This state is saved in a private boolean variable active:
Just in case we need to reprogram the Arduino board of the robotic arm in the future, we are also initializing the requested movements. This way it is clear: in order for the robotic arm to press Enter key on the terminal, the character that should be sent via Bluetooth is ‘e’.
And this happens when the public method pressEnter() is used in the test case.
We also have some waiting times that need to be adjusted. The movement is not instantaneous; it takes a while for the robotic arm to move from its initial position, touching the screen at a predetermined coordinate and moving back its initial position.
There are similar methods for all the other actions, such as inserting and extracting the card from the terminal and entering the PIN: see Appendix 5.
As previously mentioned, having the robotic arm set to active or inactive determines the behavior of our automated tests. Active means sending the commands to the robotic arm. For inactive, the automated tests will tell you, the user, what to do. Just in case you want to develop a new test case but the robotic arm is busy or taking a coffee break. This logic is done in that write() method: see Appendix 6.
Of course, you are aware when you are not going to use the robotic arm; you carefully monitor the stdout and react to the specific message by performing manually the required action.
A test-case template
Having the Bluetooth class and the AppBase class that creates the Bluetooth object → robocop (which initializes the communication with the robotic arm) the test cases will generally look something like this:
And the beauty of this is that, when you implement your test case, you don’t need to worry about the robotic arm. You just deactivate it by leaving the bluetooth.present environment variable to its default value: false. Or, if you decide to use it, you don’t have to worry about the implementation, once it is working.
Now you just launch the test or test suite and watch it do all the work.
You can see the Proof of Concept in action in this video:
We deliver faster. We deliver quality. These are our goals. For the testers in the Android team this means moving forward from this PoC to more reliable hardware, like this one from servocity.com.
Having this hardware setup with a robotic arm controlled from inside the automation framework has huge benefits. We know now that the product we are delivering is high quality at the end of each sprint because we can execute an entire test suite every night on the daily build. And since issues are discovered sooner, they are also fixed faster by developers. This means more time for other features. And overall, there is a huge confidence boost for the entire team.