Run C# tests when device Slots are limited
Below you can find information on how to organize C# test execution in case of limited device Slots.
Prerequisites
- Existing test, which is written using NUnit framework.
- List of devices, where these tests should be executed.
- Plan with limited device Slots for the Test Automation.
Parametrize the device UDID
To ensure that tests will be executed on a particular device, it’s better to parametrize the device UDID in a TestFixture.
Below you can find an example for a Set of devices.
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Support.UI;
using System;
using System.Reflection;
namespace AppiumTests
{
[TestFixture("4C071FDAP002SW")]
[TestFixture("26281JEGR04493")]
[TestFixture("R5CY23ZSTNF")]
[TestFixture("28101FDH3009BP")]
[Parallelizable(scope: ParallelScope.All)]
public class AndroidWebParallelTest(string udid)
{
....
}
}
Take a Device before the test execution
The device should be taken in a SetUp method before executing tests.
Below you can find an example of such method, which operates with a parametrized device UDID.
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Support.UI;
using System;
using System.Reflection;
namespace AppiumTests
{
....
[Parallelizable(scope: ParallelScope.All)]
public class AndroidWebParallelTest(string udid)
{
// Constants
const string ProjectName = "<TEAM_CODE>";
const string AccessKey = "mobitru_ak_...";
const string AppiumHost = "app.mobitru.com";
const string BrowserName = "chrome";
private AndroidDriver driver;
private readonly string _udid = udid;
[SetUp]
public void Setup()
{
var appiumOptions = new AppiumOptions();
appiumOptions.AutomationName = "UIAutomator2";
appiumOptions.PlatformName = "Android";
appiumOptions.BrowserName = BrowserName;
....
appiumOptions.AddAdditionalAppiumOption("udid", _udid);
var appiumHubUrl = $"""https://{ProjectName}:{AccessKey}@{AppiumHost}/wd/hub""";
driver = new AndroidDriver(new Uri($"{appiumHubUrl}"), appiumOptions);
}
....
}
}
Release the Device after
The Device should be released after the test execution to make the device Slot free.
Below you can find an example of a TearDown method, where a Device is released by terminating a WebDriver session.
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Support.UI;
using System;
using System.Reflection;
namespace AppiumTests
{
....
[Parallelizable(scope: ParallelScope.All)]
public class AndroidWebParallelTest(string udid)
{
// Constants
const string ProjectName = "<TEAM_CODE>";
const string AccessKey = "mobitru_ak_...";
const string AppiumHost = "app.mobitru.com";
const string BrowserName = "chrome";
private AndroidDriver driver;
private readonly string _udid = udid;
....
[TearDown]
public void TearDown()
{
driver?.Quit();
}
}
}
Set the ‘LevelOfParallelism’ attribute to the number of threads
The LevelOfParallelism assembly-level attribute allows managing the number of simultaneous threads.
The number of threads, here, should be related to the number of available Slots. In this case, avoiding issues with concurrent device usage is possible.
Below you can find a complete example of a Test with the ‘LevelOfParallelism‘ attribute for 2 available Slots.
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Support.UI;
using System;
using System.Reflection;
[assembly:LevelOfParallelism(2)]
namespace AppiumTests
{
[TestFixture("4C071FDAP002SW")]
[TestFixture("26281JEGR04493")]
[TestFixture("R5CY23ZSTNF")]
[TestFixture("28101FDH3009BP")]
[Parallelizable(scope: ParallelScope.All)]
public class AndroidWebParallelTest(string udid)
{
// Constants
const string ProjectName = "<TEAM_CODE>";
const string AccessKey = "mobitru_ak_...";
const string AppiumHost = "app.mobitru.com";
const string BrowserName = "chrome";
const string OpenUrl = "https://mobitru.com/";
const int DefaultPageLoadTimeoutSec = 30;
const int DefaultImplicitTimeoutSec = 3;
const int DefaultWaitTimeoutSec = 10;
private AndroidDriver driver;
private readonly string _udid = udid;
[SetUp]
public void Setup()
{
var appiumOptions = new AppiumOptions();
appiumOptions.AutomationName = "UIAutomator2";
appiumOptions.PlatformName = "Android";
appiumOptions.BrowserName = BrowserName;
appiumOptions.AddAdditionalAppiumOption("udid", _udid);
var appiumHubUrl = $"""https://{ProjectName}:{AccessKey}@{AppiumHost}/wd/hub""";
driver = new AndroidDriver(new Uri($"{appiumHubUrl}"), appiumOptions);
driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(DefaultPageLoadTimeoutSec);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(DefaultImplicitTimeoutSec);
}
[Test]
public void TestAppiumDemo()
{
Assert.That(driver.Context, Is.EqualTo("CHROMIUM"), $"Focus is not on '{BrowserName}'");
// Navigate to the URL
driver.Navigate().GoToUrl(OpenUrl);
// Wait for the Mobitru main page to load
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(DefaultWaitTimeoutSec));
By demoButtonLocator = By.CssSelector("button[class*='demo']");
wait.Until(d => d.FindElement(demoButtonLocator).Displayed);
// Verify the current URL
Assert.That(driver.Url, Is.EqualTo(OpenUrl), "Current URL is incorrect.");
// Verify the page title
string expectedTitle = "Mobitru: Intelligent Real Device Cloud for Mobile and Cross-Browser Testing";
Assert.That(driver.Title, Is.EqualTo(expectedTitle), "Page title is incorrect.");
}
[TearDown]
public void TearDown()
{
driver?.Quit();
}
}
}