UI Automation

Xamarin.UITest is a testing framework that allows Automated UI Tests written in C# using the NUnit testing framework. It is a framework which is useful to automate iOS and Android applications. It integrates tightly with Xamarin.iOS and Xamarin.Android projects but it can also be used with iOS and Android projects that are written natively in Objective-C/Swift and Java. Xamarin.UITest is the Automation Library that allows the NUnit tests to execute on .apk and .ipa file. This automation framework interacts with the application such as a user interacts with the application. This framework feeds each control like "Labels, Textbox, Images, Button" etc., and perform events like "Tap, Swipe Gesture, enter values in the textbox," etc.
 
Xamarin.UITest is written as a method that is referred to as a test. The class which contains the test is known as a test fixture. The test fixture contains either a single test or a logical grouping of tests and is responsible for any setup to make the test run and any cleanup that needs to be performed when the test finishes. Each test should follow the 3 main pillars of the automation as below.
  1. Arrange - Automation is the process of some action based on conditions and initialization of the components.
  2. Act - Act is a process of interaction with the application as like human insert value into the textbox, tap on buttons and all.
  3. Assert - Assert is the process of cross-examination of the result of Act performed with the help of Arrange. It is a process of the match Act's result with a pre-define expected result. 

What is Xamarin.UITest API?

 
Xamarin Unit test interacts with the mobile application through an interface Xamarin.UITest.IApp. This interface defines methods which are responsible for the collaboration of the application and user interface. This is mainly useful for two platforms like iOS and Android. 
  • Xamarin.UITest.iOS.iOSApp This class is used to perform automation tests on the iOS application.
  • Xamarin.UITest.Android.AndroidApp This class is used to perform automation on the Android application.
iOS and Android Apps are not instantiated directly. Both types of applications are instantiated with the help of ConfigureApp class. This class ensures that the iOS or Android app is instantiated properly.
 
Mostly IApp interface is used for each test case performed in the automation. This interface is to prevent spilling one test into another. It is useful to maintain the purity of the test case. Generally, the NUnit test initializes an instance of IApp in the below two places.
  • In the SetUp method Typically, a test fixture is a logical grouping of related tests, each of them running independently of the other. In this scenario the IApp should be initialized in the SetUp method, ensuring that a new IApp is available for each test.
  • In the TestFixtureSetup method, in some situations, a single test may require its own test fixture. In this case, it may make more sense to initialize the IApp object once in the TestFixtureSetup method.
AppInitializer class contains the initialization of the app according to the platform.
  1. public class AppInitializer {  
  2.  public static IApp StartApp(Platform platform) {  
  3.   if (platform == Platform.Android) {  
  4.    IApp iApp = ConfigureApp  
  5.     .Android  
  6.     .ApkFile("/Users/Documents/ABC/ABC.Android/bin/Release/ABC.apk")  
  7.     //Apk file path    
  8.     .Debug()  
  9.     .EnableLocalScreenshots()  
  10.     .StartApp(Xamarin.UITest.Configuration.AppDataMode.Clear);  
  11.    return iApp;  
  12.   } else {  
  13.    IApp app = ConfigureApp  
  14.     .iOS  
  15.     .StartApp();  
  16.    return app;  
  17.   }  
  18.  }  
  19. }  
Mostly, the IApp interface is used for each test case performed in the automation. This interface is to prevent spilling one test into another. It is useful to maintain the purity of the test case. Generally, the NUnit test initializes an instance of IApp in below two places.
  • TextFixture is a grouping of same test cases. Each Text fixture runs independently. In this case, IApp should be initialized in the Setup method of each test fixture. So, each test fixture has its own instance that maintains the purity of the test fixture.

  • TestFixtureSetup Some scenario single test may require its own test fixture. In that case, we should initialize the IApp object into the TestFixtureSetup method.
Test Class describes all the test cases as methods.
  1. [TestFixture(Platform.Android)]  
  2. [TestFixture(Platform.iOS)]  
  3. public class Tests  
  4. {  
  5.     protected IApp app;  
  6.     protected Platform platform;  
  7.     Pages.LoginPage loginPageObj { getset; }  
  8.     Pages.GuideViewPage guideViewPageObj { getset; }  
  9.     Pages.HamburgerMenuPage hamrgerMenuPageObj { getset; }  
  10.     public Tests(Platform platform)  
  11.     {  
  12.         this.platform = platform;  
  13.     }  
  14.   
  15.     [SetUp]  
  16.     public void BeforeEachTest()  
  17.     {  
  18.         if (this.app == null)  
  19.         {  
  20.             this.app = AppInitializer.StartApp(platform);  
  21.             loginPageObj = new LoginPage(this.app, this.platform);  
  22.             hamrgerMenuPageObj = new HamburgerMenuPage(this.app, this.platform);  
  23.         }  
  24.         else  
  25.             hamrgerMenuPageObj.DoLogout(this.platform);  
  26.   
  27.         loginPageObj.DoLogin();  
  28.        }  
  29.   
  30.        [TearDown]  
  31.        public void AfterEachTest()  
  32.        {  
  33.            hamrgerMenuPageObj.DoLogout(this.platform);  
  34.        }  
BeforeEachTest method is decorated with [Setup] Attribute, which allows basic setup before executing testcase.
 
AfterEachTest method is decorated with [TearDown] Attribute, which executes after each test case so basically we use it for logging out of the app after each test case.
 
Now, let's move to write a test case.
  1. #region Test Cases    
  2.  [Test]    
  3.  public void A_VerifyLoginPageTestCase()    
  4.  {    
  5.      Boolean result = true;    
  6.      var UserName = loginPageObj.ValidateTextOnLoginScreen("placeholder""username");    
  7.      Assert.That(UserName, Is.EqualTo(result));    
  8.   
  9.      var Password = loginPageObj.ValidateTextOnLoginScreen("placeholder""password");    
  10.      Assert.That(Password, Is.EqualTo(result));    
  11.   
  12.      var LogInButton = loginPageObj.ValidateTextOnLoginScreen("id""submitButton");    
  13.      Assert.That(LogInButton, Is.EqualTo(result));    
  14.   
  15.  }    
LoginPage class example,
  1. class LoginPage : BasePage  
  2.     {  
  3.         Platform platform;  
  4.         public LoginPage(IApp app, Platform platform) : base(app, "LoginPage")  
  5.         {  
  6.             this.platform = platform;  
  7.         }  
  8.   
  9.         public void DoLogin(String username = "Username", String password = "Password", Boolean valid = true)  
  10.         {  
  11.             Query LoginHeader = x => x.Id("Login");   
  12.             AppInitializer.StartApp(platform);   
  13.             BasePageTests.WaitForWebElement(this.app, this.app.Query(x => x.XPath("//input[@name='email']")), 30);  
  14.             BasePageTests.WaitForWebElement(this.app, this.app.Query(x => x.XPath("//input[@name='password']")), 10);  
  15.             BasePageTests.WaitForWebElement(this.app, this.app.Query(x => x.XPath("//button[@type='submit']")), 10);  
  16.             this.FocusOnEmailField(username);  
  17.             this.FocusOnPasswordField(password);  
  18.             this.ClickOnSubmitButton();  
  19.   
  20.             if (valid == false)  
  21.             {  
  22.                 BasePageTests.WaitForWebElement(this.app, this.app.Query(x => x.XPath("//h3")));  
  23.             }  
  24.             else  
  25.                 this.app.WaitForElement(x => x.Marked("Dashboard"), "Dashboard Page is not displaying in 60 seconds", new TimeSpan(0,0,60,0); );  
  26.         }
  27. }  
Here, I am using 1 abstract class for basic settings
  1. abstract class BasePage {  
  2.  #  
  3.  region Constructors  
  4.  protected BasePage(IApp app, string pageTitle) {  
  5.   this.app = app;  
  6.   PageTitle = pageTitle;  
  7.  }#  
  8.  endregion  
  9.  
  10.  # region Properties  
  11.  public string PageTitle {  
  12.   get;  
  13.  }  
  14.  protected IApp app {  
  15.   get;  
  16.  }#  
  17.  endregion  
  18.  
  19.  # region Methods  
  20.  public virtual void WaitForPageToLoad() => app.WaitForElement(x => x.Marked(PageTitle));#  
  21.  endregion  
  22. }  
1 static class for maintaining static funtion of the automation
  1. public class BasePageTests {  
  2.  public static Boolean IsElementPresent(IApp app, Query locatorQuery, int waitInSeconds) {  
  3.   try {  
  4.    app.WaitForElement(locatorQuery, "Failed to find the " + locatorQuery + " in " + waitInSeconds + " seconds."new TimeSpan(0, 0, 60, 0));  
  5.    int count = app.Query(locatorQuery).Length;  
  6.    return (count == 1);  
  7.   } catch (Exception ex) {  
  8.    Console.Write(ex.StackTrace);  
  9.   }  
  10.   return false;  
  11.  }  
  12. }  
To get the mobile screen controls we have REPL (Read Eval Print Loop) method which opens the console with application details.
 
In the console window, type tree. This will open a screen in a tree view as per the image.