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 ThreadLocalconcurrentApp = 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 ThreadLocalconcurrentApp = 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