Develop ADB Shell Commands Library Appium C#
In the last article from the Appium Series, we looked into a long list of useful ADB commands that you can use to control Android devices through CMD. In this article, we will continue the subject by looking at how you can add additional functionality to Appium Driverif something is not implemented natively.
What Is ADB?
ADB, or Android Debug Bridge, is a command-line utility included with Google's Android SDK. ADB can control your device over USB from a computer, copy files back and forth, install and uninstall apps, run shell commands, and more.
How to Execute ADB Shell Commands Via Appium
I read about this possibility for the first time in this AppiumPro article,which was originally written in Java but I decided to play around with it and try it in C#.
Note: Keep in mind that I found that this functionality is not very stable lately and sometimes is not working at all in the latest version of the C# bindings. I hope that the Appium maintainers will stabilize it soon.
First, you need to start Appium in relax security mode. Since Appium 1.7.2, there is a flag called --relaxed-security
, which you can call while starting the Appium server.
Start Appium Desktop in Relaxed Security
First, you need to start Appium Desktop as Administrator. Next, click on the Advanced tab and check Relaxed Security.
Start Appium Server in Relaxed Security CLI
1. Download and install the Node and NPM tool.
2. InstallAppium through the command line
npm install -g appium
3. StartAppium Server
appium --relaxed-security
Start Appium Server in Relaxed Security C# Code
You can start the Appium Server directly from your C# code. You can use the below snippet.
[TestClass]
public class AppiumTests
{
private static AndroidDriver<AndroidElement> _driver;
private static AppiumLocalService _appiumLocalService;
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
var args = new OptionCollector()
.AddArguments(GeneralOptionList.PreLaunch())
.AddArguments(new KeyValuePair<string, string>("--relaxed-security", string.Empty));
_appiumLocalService = new AppiumServiceBuilder().WithArguments(args).UsingAnyFreePort().Build();
_appiumLocalService.Start();
string testAppPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "ApiDemos-debug.apk");
var appiumOptions = new AppiumOptions();
appiumOptions.AddAdditionalCapability(MobileCapabilityType.AutomationName, "UiAutomator2");
appiumOptions.AddAdditionalCapability(MobileCapabilityType.DeviceName, "android25-test");
appiumOptions.AddAdditionalCapability(AndroidMobileCapabilityType.AppPackage, "com.example.android.apis");
appiumOptions.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
appiumOptions.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "7.1");
appiumOptions.AddAdditionalCapability(AndroidMobileCapabilityType.AppActivity, ".ApiDemos");
appiumOptions.AddAdditionalCapability(MobileCapabilityType.App, testAppPath);
_driver = new AndroidDriver<AndroidElement>(_appiumLocalService, appiumOptions);
_driver.CloseApp();
}
[TestInitialize]
public void TestInitialize()
{
_driver?.LaunchApp();
_driver?.StartActivity("com.example.android.apis", ".ApiDemos");
}
[TestCleanup]
public void TestCleanup()
{
_driver?.CloseApp();
}
[ClassCleanup]
public static void ClassCleanup()
{
_appiumLocalService.Dispose();
}
// tests...
}
The most important part is where we supply the KeyValuePair
for the relaxed security flag.
var args = new OptionCollector()
.AddArguments(GeneralOptionList.PreLaunch())
.AddArguments(new KeyValuePair<string, string>("--relaxed-security", string.Empty));
_appiumLocalService = new AppiumServiceBuilder().WithArguments(args).UsingAnyFreePort().Build();
_appiumLocalService.Start();
Use ExecuteScript to Run Shell ADB Commands
Appium maintainers created a 'backdoor' for us for running ADB shell commands through the ExecuteScript
method. We need to run it with a first argument equal to mobile: shell
and the second one in JSON format, which contains the information about the actual shell ADB command.
If we want to execute the following command: adb shell dumpsys battery reset
(which resets the state of the battery), we need to provide a JSON in the following format: {"command": "dumpsys", "args": ["battery", "reset"]}. So, we need to provide the actual command, and the rest of the parameters are passed as the JSON array args.
[TestMethod]
public void PerformRandomShellCommandAsJson()
{
string result = _driver.ExecuteScript("mobile: shell", "{"command": "dumpsys", "args": ["battery", "reset"]}").ToString();
Debug.WriteLine(result);
}
Develop ADB Android Appium Driver Library
Using pure JSON in C# is pretty unreadable. We are smart and can use the .NET serialization libraries to make our code prettier. For the job, first, we create a new class called AdbCommand
.
public class AdbCommand {
public AdbCommand(string command) {
Command = command;
Args = new List < string > ();
}
public AdbCommand(string command, params string[] args) {
Command = command;
Args = new List < string > (args);
}
[JsonProperty("command")]
public string Command {
get;
set;
}
[JsonProperty("args")]
public List < string > Args {
get;
set;
}
public override string ToString() {
return JsonConvert.SerializeObject(this);
}
}
Also, I installed an additional C# library called Newtonsoft.Json
, which simplifies the JSON serialization and deserialization. We use the JsonProperty
attributes to tell the library how to name these properties in the result JSON. Also, we override the ToString
method, and there, we return the whole object as JSON through Newtonsoft.Json SerializeObject
method.
Here is how looks the same code through the usage of the new class.
[TestMethod]
public void PerformRandomShellCommand()
{
string result = _driver.ExecuteScript("mobile: shell", new AdbCommand("dumpsys", "battery", "reset").ToString()).ToString();
Debug.WriteLine(result);
}
Create Appium Driver ADB Extension Methods
To reuse some code and make the usage of the new methods much easier, we can create extension methods to the AndroidDriver
.
public static class AppiumDriverAbdExtensionMethods {
public static string GetLogs(this AndroidDriver < AndroidElement > androidDriver) {
return ExecuteShellAdbCommand(androidDriver, "logcat");;
}
public static void ChangeBatteryLevel(this AndroidDriver < AndroidElement > androidDriver, int level) {
ExecuteShellAdbCommand(androidDriver, $ "dumpsys battery set level {level}");
}
public static void ChangeBatteryReset(this AndroidDriver < AndroidElement > androidDriver) {
ExecuteShellAdbCommand(androidDriver, "adb shell dumpsys battery reset");
}
public static string GetBatteryStatus(this AndroidDriver < AndroidElement > androidDriver) {
return ExecuteShellAdbCommand(androidDriver, "adb shell dumpsys battery");;
}
private static string ExecuteShellAdbCommand(AndroidDriver < AndroidElement > androidDriver, string command, params string[] args) {
return androidDriver.ExecuteScript("mobile: shell", new AdbCommand(command, args).ToString()).ToString();
}
}
Here is how we use the new API.
[TestMethod]
public void PerformShellCommandViaExtensionMethod()
{
_driver.ResetBattery();
}