Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Writing Xamarin.UITest Tests with Base Classes and ScreenObject

I hope the previous blog post of the Xamarin.UITest 101 series provides some insight into how to write and run UI tests on Android and iOS platforms. Generally, writing automated tests is pretty easy, but writing solid tests will be much harder as the size and complexity of the project grow incrementally. Thus, in this blog post we’ll look at some advanced approaches to writing more scalable and maintainable Xamarin.UITest tests.

Creating a Base Class for Xamarin.UITest Tests

As far as the number of Xamarin.UITest tests grow, it gets more and more challenging to scale the project, as well as save test method and initialize app startup in the test Class. We have to set this initialization into another class in order to reuse the initialization code in other tests. In such a way, it will allow us to extend test cases and enlarge the test suite more easily.

Let’s first create a folder named Base where we can hold classes with basic configurations. In this folder, let’s also create a BaseTest class with the basic configurations.

public class BaseTest
{
  public static IApp app;
  Platform platform;

  private static ThreadLocal concurrentApp = new ThreadLocal();

  public static IApp GetApp()
  {
    return concurrentApp.Value;
  }

  public BaseTest(Platform platform)
  {
    this.platform = platform;
  }

  [SetUp]
  public void BeforeEachTest()
  {
    concurrentApp.Value = AppInitializer.StartApp(platform);
  }
}
private static ThreadLocal concurrentApp = new ThreadLocal();

With this configuration, we can run several tests simultaneously. It also ensures that other test cases won’t be closed before the due time, and the rest of the code will be moved from the test class.

Using ScreenObject for Further Optimization

To make this even more optimized, we can use ScreenObject pattern to describe components of the app screens.

BaseScreen

Now let’s create a BaseScreen class that will describe basic actions with the elements of our Xamarin.Forms app.

class BaseScreen
{

  protected void Tap(Func query)
  {
    BaseTest.GetApp().Tap(query);
  }

  protected void WaitForElement(Func query)
  {
    BaseTest.GetApp().WaitForElement(query);
  }

  protected void Type(string text, Func query)
  {
    BaseTest.GetApp().ClearText(query);
    BaseTest.GetApp().EnterText(query, text);
  }

  protected bool IsElementPresentByText(string text)
  {
    return BaseTest.GetApp().Query(x => x.Text(text)).Any();
  }
}

Here, Func query is a parameter that describes the locator of the element that we need.

MainScreen and AddingItemScreen

Then, we need to create the MainScreen and AddingItemScreen classes that will describe the main screen and the screen with adding a new item to our app. Also, here we describe a few methods for interaction with screen elements.

In addition, we need a Screen class that will be a factory class and will return instances of some pages.

This is a factory class that will return instances of the screens that we need.

class Screen
{

  private static MainScreen mainScreen;
  private static AddingItemScreen addingItemScreen;

  public static MainScreen Main => mainScreen ?? (mainScreen = new MainScreen());
  public static AddingItemScreen AddingItem => addingItemScreen ?? (addingItemScreen = new AddingItemScreen());

}

The MainScreen class describes the main screen of our application. Previously, we’ve created the BaseScreen class with the basic methods. MainScreen inherits from the BaseScreen class. And now we can use the methods described there to interact with the page elements.

class MainScreen : BaseScreen
{
  //Add button locator
  Func addButton = x => x.Marked("Add");

  //Waiting Add button
  public void WaitForAddButton()
  {
    WaitForElement(addButton);
  }

  //Clicking Add button
  public void ClickAddButton()
  {
    Tap(addButton);
  }

  // Checking is item name present in the list
  public bool IsItemNamePresent(string ItemName)
  {
    return IsElementPresentByText(ItemName);
  }

  // Checking is item description present in the list
  public bool IsItemDescriptionPresent(string ItemDescription)
  {
    return IsElementPresentByText(ItemDescription);
  }
}

Same for AddingItemScreen

class AddingItemScreen : BaseScreen
{
  private readonly Func itemNameField = x => x.Marked("item_name_Container").Descendant().Index(1);
  private readonly Func itemDescriptionField = x => x.Marked("item_description_Container").Descendant().Index(1);
  private readonly Func saveButton = x => x.Marked("save_button");

  //Setting item name
  public void SetItemName( string ItemName)
  {
    Type(ItemName, itemNameField);
  }

  //Setting item description
  public void SetItemDescription(string ItemDescription)
  {
    Type(ItemDescription, itemDescriptionField);
  }

  //Tapping Save button
  public void TapSaveButton()
  {
    Tap(saveButton);
  }

  //Waiting Save button
  public void WaitForSaveButton()
  {
    WaitForElement(saveButton);
  }
}

After we describe all the necessary screens and methods that return instances of the needed classes, we can rewrite the test using a new structure of our project.

public class Tests : BaseTest
{
  public Tests(Platform platform) : base(platform)
  {
  }

  [Test]
  public void ItemCreationTest()
  {
    // Generating random item name and description using tempale:
    // item_name + timestamp
    // item_description + timestamp
    String itemName = "item_name" + new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
    String itemDescription = "item_description" + new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();

    //Waiting 'ADD' button
    //app.WaitForElement(x => x.Marked("Add"));

    //Waiting 'ADD' button
    Screen.Main.WaitForAddButton();
    // Tap 'ADD' button
    Screen.Main.ClickAddButton();

    //Waiting 'Save' button
    Screen.AddingItem.WaitForSaveButton();

    //Set text to 'Item name' text field
    Screen.AddingItem.SetItemName(itemName);
    //Set text to 'Item description' text field
    Screen.AddingItem.SetItemDescription(itemDescription);
    //Tap 'Save' button
    Screen.AddingItem.TapSaveButton();

    //Verifying item with generated itemName is present
    Assert.IsTrue(Screen.Main.IsItemNamePresent(itemName));

    //Verifying item with generated itemDescription is present
    Assert.IsTrue(Screen.Main.IsItemDescriptionPresent(itemDescription));

  }
}

Finally, if we want to initiate the application startup but the methods of the Screen class are static, we can request them without creating class instances and we’ll get short records of the requests.

Screen.Main.WaitForAddButton();
Assert.IsTrue(Screen.Main.IsItemNamePresent(itemName));

This structure of the project allows us to edit screen class descriptions without effort. Also, it will be much easier to increase a project if we have separated components: screens, configurations, tests.

The post Writing Xamarin.UITest Tests with Base Classes and ScreenObject appeared first on Bitbar.



This post first appeared on Mobile App Testing Blog, With Games And Web | Test, please read the originial post: here

Share the post

Writing Xamarin.UITest Tests with Base Classes and ScreenObject

×

Subscribe to Mobile App Testing Blog, With Games And Web | Test

Get updates delivered right to your inbox!

Thank you for your subscription

×