Skip to main content

Managing Android emulator in .NET

Automated coded UI testing become very popular in the past few years. Its allow developers to create tests which are executed directly on UI level and simulate user actions. At the same time HTML5 become a standard for creating universal modern applications which can be hosts in a native browser controls.

New model of creating applications brings a new challenges in the testing fields that's why in this post I want to present my solution (it takes me almost two days to get this working!) which is first step in the process of creation an end-to-end test automation for mobile applications. My solution is prototype of a .NET console application which can be use to control Android emulator and simulate user standard operation like installing app, typing and rotating. This prototype can be use as 'emulator manager' which controls device emulator on which tests are performed - for example by using  Selenium.

As initial requirements to run the project:
1) Install the latest Java build :(  (sorry .NET geeks but it`s required)
2) Install Android SDK

Now we have all environment in place so we can create our emulator device. To do that we need to use Android Virtual Devices Manager tool which is located in the '..\\sdk\tools\' path inside Android SDK folder. To run this tool we need start cmd, navigate to the SDK tools folder and type 'android avd'. Now we can create a new emulator by specifying all setting we need - in the picture below I presented settings for my AVD called 'nexus'. To make sure that all configuration if correct we should run out AVD by calling emulator.exe  from  '..\\sdk\tools\'  for example 'emulator -avd nexus' - this cause our emulator start running and first run might take some time so be patient.

Picture 1. Configuration of a new  device emulator.
Now its time to back to the C# code a present solution which can manage Android emulator through .NET code. Basically solution based on the simple console application which is calling externals console applications:

  • emulator.exe - console application which allow to start device emulator by device name with specific parameters.
  • adb.exe - console application which allow communication with device emulator; allow to run shell mode directly on the emulator.
Picture 2. Application architecture.

There is two very important parts inside this solution. First one is the IEmulator interface which expose all functions which are currently supported by the program. The idea is that this solution can be extended to all main three mobile platforms (iOS, Android, Windows) and the IEmulator interface will remain common an contract for all of them so just implementation of each emulator will be different (now only AndroidEmulator class is implemented). IEmulator interface implementation is as follow:

Code Snippet
  1. /// <summary>
  2. /// Expose all emulator tasks.
  3. /// </summary>
  4. public interface IEmulator
  5. {
  6.     /// <summary>
  7.     /// Starts the emulator.
  8.     /// </summary>
  9.     /// <returns>Value which determines emulator is ready for use.</returns>
  10.     bool Initialize();
  11.  
  12.     /// <summary>
  13.     /// Enable shell mode on the emulator.
  14.     /// </summary>
  15.     void StartShell();
  16.  
  17.     /// <summary>
  18.     /// Checks the emulator is running.
  19.     /// </summary>
  20.     bool IsRunning();
  21.  
  22.     /// <summary>
  23.     /// Install app on the emulator.
  24.     /// </summary>
  25.     /// <param name="packagePath">Path to the apk package to intsall.</param>
  26.     /// <returns>True if application installed correctly.</returns>
  27.     bool InstallApp(string packagePath = null);
  28.  
  29.     /// <summary>
  30.     /// Uninstall specyfic package from emulator.
  31.     /// </summary>
  32.     /// <param name="packageName">Specyfic package name to uninstall.</param>
  33.     void UninstallApp(string packageName = null);
  34.  
  35.     /// <summary>
  36.     /// Run monkey on package.
  37.     /// </summary>
  38.     /// <param name="activitiesCount">Action count.</param>
  39.     void RunMonkey(int activitiesCount = 500);
  40.  
  41.     /// <summary>
  42.     /// Simulating keypress events on emulator.
  43.     /// </summary>
  44.     /// <param name="codes">Hardware codes array.</param>
  45.     void TypeAsHardwareInput(int[] codes);
  46.  
  47.     /// <summary>
  48.     /// Change orientation of the emulator.
  49.     /// </summary>
  50.     void ChangeOrientation();
  51. }

As you can see here, not all options are implemented but the most important functions are on place so let`s have a quick look on its:
  • Initialize: function which starts device emulator by calling emulator.exe as separate process and pass AVD name from the configuration file.
  • StartShell:  using 'adb shell' command from adb.exe tool in separated process to start shell mode on the currently running AVD. Process which hosts shell will remain active until application close.
  • IsRunning: function which call  'adb devices'  command  in separated process from adb.exe tool to check if AVD  is currently running.
  • InstallApp: install APK file from specific path in local PC by calling 'adb wait-for-device install <apk full-path>' command from adb.exe as separated process. Additional wait-for-device command ensure that emulator is ready to install app. 
  • UninstalApp: uninstall app from shell level by using package name (ex. com.android.tools.sdkcontroller stored in AndroidInstalledAppName App.config)
  • RunMonkey: from shell mode run monkey random actions on the installed package by executing N (activitiesCount) random activities.
  • TypeAsHardwareInput: allow to use simulate hardware keys to input any supported input. Under the hood its calling shell command 'input keyevent <key_code>' with key code specified for the platform. Key-codes list is available here.
Last function I want to describe in not supported by the Android SDK and from testing point of view it`s crucial -I mean here changing emulator orientation during the run-time. I spent a lot of time reading and investigating any potential possibility of changing emulator orientation by no of solutions I`ve found  was working for me. There is only one official way to switch emulator orientation - by pressing CTRL+F11, and I`m implemented it. Source code for this using  an external library called InuputSimulator (thank you Michael) to simulate keystroke. Additionally I used emulator process  name to find exact process and get focus on their main window before simulate keystroke.   

Code Snippet
  1. [DllImport("user32.dll")]
  2.         static extern bool SetForegroundWindow(IntPtr hWnd);
  3.  
  4.         /// <summary>
  5.         /// Change orientation of the emulator.
  6.         /// </summary>
  7.         public void ChangeOrientation()
  8.         {
  9.             var emulatorProcess = Process.GetProcessesByName("emulator-arm");
  10.             if (emulatorProcess.Count() == 0)
  11.             {
  12.                 throw new InvalidOperationException("Unable to find emulator process.");
  13.             }
  14.  
  15.             BringToFront(emulatorProcess[0]);
  16.             WindowsInput.InputSimulator.SimulateModifiedKeyStroke(WindowsInput.VirtualKeyCode.CONTROL, WindowsInput.VirtualKeyCode.F11);
  17.         }
  18.         
  19.         /// <summary>
  20.         /// Set focus on the process.
  21.         /// </summary>
  22.         /// <param name="pTemp">Process to be focused.</param>
  23.         private void BringToFront(Process pTemp)
  24.         {
  25.             SetForegroundWindow(pTemp.MainWindowHandle);
  26.         }

Now when all functions are on place we can call them in logical order.

Code Snippet
  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         var emulator = EmulatorFactory.GetEmulator(EmulatorType.Android);
  6.  
  7.         emulator.Initialize();
  8.  
  9.         emulator.InstallApp();
  10.  
  11.         emulator.StartShell();
  12.  
  13.         emulator.ChangeOrientation();
  14.         emulator.ChangeOrientation();
  15.  
  16.         emulator.RunMonkey();
  17.         emulator.UnistallApp();
  18.     }
  19. }

I can`t guarantee but I hope that soon I will add next emulator - keep your fingers crossed!

Whole source code of the project is available here.

Thank you

More info:

Popular posts from this blog

Full-Text Search with PDF in Microsoft SQL Server

Last week I get interesting task to develop. The task was to search input text in PDF file stored in database as FileStream. The task implementation took me some time so I decided to share it with other developers. Here we are going to use SQL Server 2008 R2 (x64 Developers Edition), external driver from Adobe, Full-Text Search technology and FileStream technology.Because this sems a little bit comlicated let`s make this topic clear and do it step by step. 1) Enable FileStream - this part is pretty easy, just check wheter You already have enabled filestream on Your SQL Server instance - if no simply enable it as in the picture below. Picture 1. Enable filestream in SQL Server instance. 2) Create SQL table to store files  - mainly ther will be PDF file stored but some others is also be allright. Out table DocumentFile will be created in dbo schema and contain one column primary key with default value as sequential GUID. Important this is out table contains FileSt...

Persisting Enum in database with Entity Framework

Problem statement We all want to write clean code and follow best coding practices. This all engineers 'North Star' goal which in many cases can not be easily achievable because of many potential difficulties with converting our ideas/good practices into working solutions.  One of an example I recently came across was about using ASP.NET Core and Entity Framework 5 to store Enum values in a relational database (like Azure SQL). Why is this a problem you might ask... and my answer here is that you want to work with Enum types in your code but persist an integer in your databases. You can think about in that way. Why we use data types at all when everything could be just a string which is getting converted into a desirable type when needed. This 'all-string' approach is of course a huge anti-pattern and a bad practice for many reasons with few being: degraded performance, increased storage space, increased code duplication.  Pre-requirements 1. Status enum type definition...

Using Newtonsoft serializer in CosmosDB client

Problem In some scenarios engineers might want to use a custom JSON serializer for documents stored in CosmosDB.  Solution In CosmosDBV3 .NET Core API, when creating an instance of  CosmosClient one of optional setting in  CosmosClientOptions is to specify an instance of a Serializer . This serializer must be JSON based and be of  CosmosSerializer type. This means that if a custom serializer is needed this should inherit from CosmosSerializer abstract class and override its two methods for serializing and deserializing of an object. The challenge is that both methods from  CosmosSerializer are stream based and therefore might be not as easy to implement as engineers used to assume - still not super complex.  For demonstration purpose as or my custom serializer I'm going to use Netwonsoft.JSON library. Firstly a new type is needed and this must inherit from  CosmosSerializer.  using  Microsoft.Azure.Cosmos; using  Newtonsoft.Json; usin...