Introduction
This step-by-step tutorial will take you through setting up your first unit test with Visual Studio and Uno Platform. If you are new to Uno Platform, it allows for creation of pixel-perfect, single-source C# and XAML apps which run natively on Windows, iOS, Android, macOS, Linux and Web via WebAssembly. Uno Platform is free and Open Source (Apache 2.0) and available on GitHub.
Why Unit Test?
- Performing unit tests is a good practice for developers.
- Running unit tests is important for good quality and maintainable code.
- A unit test helps find errors in your code in a very easy manner.
- Unit testing helps reduce the cost of bug fixes.
Prerequisites
- Visual Studio with Uno Platform
Create a New Uno Project
Open Visual Studio and click on Create new project.
Search for the Uno templates, select the Cross-Platform App (Uno Platform) then click Next.
Name your app. You probably want your project and solution to use the same name as your app. Put it on your preferred location for projects and click “Create”.
Uno.UITest for Uno Platform
The testing is available through:
- Selenium for WebAssembly apps, using Chrome
- Xamarin.UITest and AppCenter for iOS and Android apps.
Note: The following target platforms are not yet supported:
- SkiaSharp backends (GTK, WPF and Tizen )
- Xamarin.macOS
- Windows
Setup Uno.UITest – your Uno Platform app
- Make sure Google Chrome is installed on your computer.
- In Visual Studio for Windows, Create a project using the Uno Platform templates.
Enable UIAutomationMapping
You need to Add the following code in your each .csproj file (iOS, Android and WebAssembly), at the end – before the closing </project> tag:
Search the .csproj files and edit them in the given below two ways:
Option 1
In Visual Studio, right-click on the Project and choose ‘Open folder’ in File Explorer.
In the file explorer, go to your Solution folder.
Search for the keyword *.csproj in the project
Edit the .csproj files (iOS, Android, and WebAssembly), at the end – before the closing </project> tag: with the code below.
<PropertyGroup Condition="'$(Configuration)'=='Debug' or '$(IsUiAutomationMappingEnabled)'=='True'"> <IsUiAutomationMappingEnabled>True</IsUiAutomationMappingEnabled> <DefineConstants>$(DefineConstants);USE_UITESTS</DefineConstants> </PropertyGroup> <ItemGroup> <!-- remove this block when using Uno.UI 3.5 or later --> <UnoSourceGeneratorAdditionalProperty Include="IsUiAutomationMappingEnabled" /> </ItemGroup>
Option 2
In the VS code, right-click on the particular platform project (.iOS, .droid, .Wasm), choose ‘Unload project’, and then edit the .csproj directly in VS.
After Unloading the project (.iOS, .Droid, .Wasm), you can start editing the .csproj in Visual Studio itself. Now add code here:
After editing the .csproj file, reload the Projects.
Xamarin.TestCloud.Agent
In the iOS project, add a reference to the Xamarin.TestCloud.Agent nuget package (0.21.8 or later).
In Visual Studio, right-click on the iOS project and choose ‘Manage Nuget Packages’.
Search for the ‘Xamarin.TestCloud.Agent’ in the ‘browse’ section, choose the ‘Xamarin.TestCloud.Agent’ option, and click the ‘Install’ button. Then check installation completed.
Search for the app.xaml.cs file and the OnLaunched method. Then add the following code at the beginning.:
#if __IOS__ && USE_UITESTS // Launches Xamarin Test Cloud Agent Xamarin.Calabash.Start(); #endif
Add UITest Project
Install the Uno Platform dotnet new templates:
dotnet new -i Uno.ProjectTemplates.Dotnet
Go to your .sln folder using a command line.
Create a folder named YourAppName\YourAppName.UITests
Then run :
cd YourAppName.UITests dotnet new unoapp-uitest
Once the script is executed, the new project will be added to your solution. And when the UI Project is installed in your solution, you can start to write the UI Test.
Constant.cs
Now, you need to edit Constant.cs. This is an auto-generated file.
- WebAssemblyDefaultUri: It represents – once you run your WebAssemply app, you will get one base URL in the browser which you need to replace. Ex: http://localhost:64555/
- iOSAppName: It’s called a bundle identifier which you get from Info.plist.
- AndroidAppName: It’s called PackageName which you get from Manifest file
public class Constants { public readonly static string WebAssemblyDefaultUri = "http://localhost:64555/"; public readonly static string iOSAppName = "com.example.app"; public readonly static string AndroidAppName = "com.example.app"; public readonly static string iOSDeviceNameOrId = "iPad Pro (12.9-inch) (3rd generation)"; public readonly static Platform CurrentPlatform = Platform.Browser; }
Create a XAML UI
Before going to create a XAML UI, you must explore the Uno Platform Documentation.
AutomationId
A unique AutomationId should be set to all UI controls that are required for UI testing.
AutomationId is a Dependency Property which can also be set with a binding expression.
InvalidOperationException will be thrown if an attempt is made to set the AutomationId property of an Element more than once.
AutomationId and X:Name are different. You must set AutomationId for all controls.
Ex: AutomationProperties.AutomationId=”MainPageSubmitBtn”
MainPage.XAML
Now, I’m creating a simple XAML UI with Radio Button. If you need to know more about the UI part, read the UWP documentation.
<Page x:Class="UnoUnitTest.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UnoUnitTest" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Content> <StackPanel> <TextBlock AutomationProperties.AutomationId="MainPageWelcomelbl" x:Name="lblWelcome" Text="Welcome to Uno Platform"/> <Button AutomationProperties.AutomationId="MainPageSubmitBtn" x:Name="btnSubmit" Content="Click me"/> <RadioButton AutomationProperties.AutomationId="radio1" GroupName="Gender" Content="Male" IsChecked="True"/> <RadioButton AutomationProperties.AutomationId="radio2" GroupName="Gender" Content="Female" IsChecked="False"/> <RadioButton AutomationProperties.AutomationId="radio3" GroupName="Gender" Content="Others" IsChecked="False"/> </StackPanel> </Page.Content> </Page>
Write a UI test
Create a class file by right-clicking the UITest project → Add New → Give name and Create.
Here, I’m going to write the UI test for RadioButton. And inherit the TestBase class.
RadioButton_Test.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NUnit.Framework; using Uno.UITest.Helpers.Queries; using Query = System.Func<Uno.UITest.IAppQuery, Uno.UITest.IAppQuery>; namespace UnoUnitTest.UITests { public class RadioButton_Test:TestBase { [Test] public void RadioButton01() { Query testSelector = q => q.Marked("MainPageRadiobtn"); App.WaitForElement(testSelector); App.Tap(testSelector); Query male = q => q.Marked("male"); Query female = q => q.Marked("female"); Query others = q => q.Marked("others"); Query results = q => q.Marked("result"); App.WaitForElement(male); App.WaitForElement(female); App.WaitForElement(others); App.WaitForElement(results); App.Screenshot("MainPageRadiobtn - Initial"); Assert.IsFalse(App.Query(q => male(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => female(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => others(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.AreEqual("", App.Query(q => results(q).GetDependencyPropertyValue("Text").Value<string>()).First()); App.Tap(male); App.Screenshot("MainPageRadiobtn - Step 1"); Assert.IsTrue(App.Query(q => male(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => female(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => others(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.AreEqual("Male", App.Query(q => results(q).GetDependencyPropertyValue("Text").Value<string>()).First()); App.Tap(female); App.Screenshot("MainPageRadiobtn - Step 2"); Assert.IsFalse(App.Query(q => male(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsTrue(App.Query(q => female(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => others(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.AreEqual("Female", App.Query(q => results(q).GetDependencyPropertyValue("Text").Value<string>()).First()); App.Tap(others); App.Screenshot("MainPageRadiobtn - Step 3"); Assert.IsFalse(App.Query(q => male(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsFalse(App.Query(q => female(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.IsTrue(App.Query(q => others(q).GetDependencyPropertyValue("IsChecked").Value<bool>()).First()); Assert.AreEqual("Others", App.Query(q => results(q).GetDependencyPropertyValue("Text").Value<string>()).First()); } } }
Run your UITest Project
WebAssembly Testing
First, deploy the WebAssembly project using Chrome and note the Url of your app.
Next, update the WebAssemblyDefaultUri property in Constants.cs.
Next, set the CurrentPlatform to Platform.Browser.
Run your UI Test project using Test Explorer. The browser windows will open automatically, and the test will execute.
Android Testing
Build and deploy your app on a simulator or device.
Update the AndroidAppName property in Constants.cs, and set your Package Name from your android manifest file.
Change CurrentPlatform to Platform.Android.
Now run your test project using Test Explorer. The application will start on the emulator, and the test method will execute.
iOS Testing
Note: If you want to run an iOS app, you need Visual studio for Mac.
Build and deploy your app on iOS simulator.
Update the iOSAppName property in Constants.cs and set your Bundle Identifier.
Change the CurrentPlatform to Platform.iOS.
Now run your test project using Test Explorer. The application will start on the simulator and the test method will execute.
Summary
I hope you have understood how to write unit tests for your Uno Platform application.
Thanks for reading. Please share your comments and feedback.
Happy Coding 🙌
Delphin Susai Raj, C# Corner MVP & Sakshi Shukla, Uno Platform team