Experitest Blog

Best practices for Selenium and Appium testing with C# and NUnit

What is the main question that arises when someone considers using automated Selenium and Appium testing on their mobile or web testing project?

“With all the effort taken in writing and supporting automated tests, does it really save that much time?”

Indeed, people that are not familiar with the best practices for automated testing are usually afraid that the expense of automated testing will outweigh the usability. Well, actually this is a perfectly valid point. Designing, writing and supporting automated tests might be a chore! Thankfully, as is true with any software development, a number of established patterns and practices can make this task seem almost trivial. Following these practices, significantly reduces the amount of time spent on coding, saving more time for test execution and analysis.

In this article, I will look at some most useful best practices in general, as well as some specific best practices for Selenium and Appium testing with C# and NUnit.

 

General test design best practices

First, let’s cover some Selenium and Appium testing best practices, suitable for any automated tests.

AAA – Arrange, Act, Assert

“Arrange, Act, Assert” is a common unit testing pattern, that can be used for any automated testing task very successfully. Basically, it states that any single test should consist of three distinctive parts: Arrange, Act, Assert. In the “Arrange” part, you should prepare your application for the test, in the “Act” part you should perform one single action and in the “Assert” part you should verify the result of that action. For example:

 

[Test]
public void Show_10_Items_On_Page()
{
// arrange
int itemsOnPage = 10;
// act
itemsList.SetNumberOfItemsOnPage(itemsOnPage );
// assert
Assert.AreEqual(itemsList.GetNumberOfItemsOnPage(), itemsOnPage);
}

 

It’s considered a good practice to have as few actions in the “Act” part as possible.  To keep a single test simple. And it’s highly recommended to use only one assertion for a single test.

Page Objects Pattern

All the while UI automated test practices exist, more and more useful patterns emerge. Patterns serve as a set of established practices, greatly improving your code by making it more clear, maintainable and modifiable. In the Page Objects pattern, main interactable parts of an application (for example, pages) are presented as an object of a class. The ways you interact with them are the methods of this class. All the calls to Selenium or Appium drivers should be obscured inside the Page Object class. For example:

 

[Test]
public void Successful_Authorization_Test()
{
// arrange
AuthorizationPage authorizationPage = homePage.Login();
// act
authorizationPage.MakeSuccessfulAuthorization();
// assert
authorizationPage.VerifyAuthorization();
}

 

In this test, we communicate with the AuthorizationPage class, that represents our target page. MakeSuccessfulAuthorization() is a method of this class, that performs successful authorization.

Naming your tests

For complex applications, there tend to be a high number of automated tests. Without a strong naming policy, it will become difficult to differentiate between them at some point. In order to make your tests accessible and reports understandable, it’s highly important to follow some rules in the test naming policy. The best practice is to make sure that the name of your test definitely describes what exactly your test verifies. For example, the following test name is not considered very informative:

 

[Test]
public void SortingCheck()

WebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(10, TimeUnit.SECONDS);

 

Whereas the following is much better:

[Test]
public void Items_Table_Check_Sorting_By_Price_Ascending()

 

Managing waiting times

One of the first things you should be aware of before writing your first automated test is how to manage wait times. Usually, a fresh page doesn’t load instantly when you press a button – it always takes some time. In automated tests, often you need to pause the execution before the next page is fully loaded. The worst way to implement waits is to use the Thread.Sleep() method, because it always pauses your tests for an exact number of milliseconds. This means that if a page is already loaded, your tests will still be paused for a definite period, so you will lose several seconds each time you invoke Thread.Sleep(). And what if a page needs more time to load than you defined? Then your test simply fails.

Fortunately, the Selenium and Appium framework provides an ability to have flexible wait times. In fact, there are two ways to manage it: implicit and explicit waits. Implicit waits set maximum wait times for the Selenium or Appium testing driver. If the element is found before the wait timer runs off, then the timeout ends. For example:

 

WebDriver driver = new FirefoxDriver();
driver.Manage().Timeouts().ImplicitlyWait(10, TimeUnit.SECONDS);

 

Explicit waits set timeouts to find specific elements in your UI. If some UI elements take longer to be loaded than others – then you should use Explicit waits. For example:

WebDriver driver = new FirefoxDriver();
driver.Get(“http://example.com”);
// wait up to 10 seconds
WebElement element = (new WebDriverWait(driver, 10))
// or until an element with Id = “bar” located
.Until(ExpectedConditions.PresenceOfElementLocated(By.Id(“bar”)));

 

Tests independence

It’s one of the basic principles of the automated test design. None of your automated tests should depend on the others. Why? Because sometimes it’s handy to run only some of the tests, and, sometimes you can’t ensure the order in which your tests being run. To preserve the desired order of execution, it’s recommended to use SetUp and TearDown annotations.

 

NUnit best practices

In this part, I want to look at the best practices specific for NUnit.

Setting Timeout for your tests

Normally, NUnit will run the test and wait, until this test is completed. In some cases, though, some tests may run indefinitely for a number of reasons. In order to prevent it, you can use the Timeout attribute:

 

[Test, Timeout(2000)]
public void PotentiallyLongRunningTest()
{
//…
}

 

Using TestCase for parameterization

Often it’s not enough to run your tests only for one set of data. Usually, it’s good to run the tests for different parameters, that can be expected (or not expected!) in the real workflow. Almost every automated testing framework provides one way or another to parametrize your tests. In NUnit it’s the TestCase attribute:

 

[TestCase(1, 2, ExpectedResult=3)]
[TestCase(2, 3, ExpectedResult=5)]
[TestCase(3, 4, ExpectedResult=7)]
public int SumTest(int x, int y)
{
return x + y;
}

 

The test method above will be run three times, and the return value will be automatically asserted to be equal ExpectedResult.

Using Range() and Values() for parameterization

Another way to parameterize your tests is to use Range() and Values() attributes. Values() attribute is used to specify a set of values for a parameter of a test method. Range() attribute specifies a range of values, as well as a step to traverse this range. For example:

 

[Test]
public void ParametrizedTest(
[Values(1, 2, 3)] int x,
[Range(0.2, 0.6, 0.2)] double d)
{
//…
}

 

The test method above will be run nine times with the following parameters:

ParametrizedTest(1, 0.2)
ParametrizedTest(1, 0.4)
ParametrizedTest(1, 0.6)
ParametrizedTest(2, 0.2)
ParametrizedTest(2, 0.4)
ParametrizedTest(2, 0.6)
ParametrizedTest(3, 0.2)
ParametrizedTest(3, 0.4)
ParametrizedTest(3, 0.6)

 

Parallel Execution

Sometimes it can be very handy to run your tests in parallel. For longer tests, it greatly reduces time and gives your team a faster feedback loop.

 

[TestFixture(“Android”, “6.0.1”, “My Description”)]
[TestFixture(“iOS”, “9.2.1”, “Another description”)]
[Parallelizable(ParallelScope.Fixtures)]
public class MyTestFixture
{
// …
}

 

In the example above, NUnit will run two test fixtures with different parameters simultaneously.

 

Selenium and Appium testing with C# NUnit

When you work with Selenium and Appium testing, your main tool is the driver. It manages to find UI elements and interactions with them. For example, that’s how you can find an element in the browser by XPath and click on it by using Selenium driver:

DesiredCapabilities dc = new DesiredCapabilities();
dc.SetCapability(CapabilityType.BrowserName, “chrome”);
RemoteWebDriver driver;
driver = new RemoteWebDriver(dc);
driver.Url = “https://example.com”;
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(3));
IWebElement manualNavLink = wait.Until(d =>d.FindElement(By.XPath(“//*[text()=’Manual’]”)));
manualNavLink.Click();

 

And here is an example of how you can find an element and click on it using Appium driver:

DesiredCapabilities dc = new DesiredCapabilities();
dc.SetCapability(“platformName”, “Android”);
dc.SetCapability(“app”, “com.example.tester.myapplication”);
dc.SetCapability(“appActivity”, “.MainActivity”);
dc.SetCapability(“appVersion”, “1.0”);

AppiumDriver driver;
driver = new AndroidDriver(dc);

driver.FindElement(By.Id(“fab”)).Click();

 

Both Selenium and Appium testing can be easily launched on the SeeTest platform – cloud-based platform, which offers a variety of browsers and mobile devices for development and web and mobile test automation. It doesn’t matter whether JAVA and jUnit or C# and NUnit are used as programming language and test framework, SeeTest enables to execute tests written in any language supported by Selenium and Appium. Furthermore, SeeTest is ranked at the top among cloud platforms for conducting web and mobile test automation.

In conclusion, when it comes to automated testing, it’s very beneficial to follow some established good practices and patterns from the beginning. It makes automated tests useful for the whole team and makes the Selenium and Appium testing easy to support and extend.

 

Konstantin Tonkov – Performance Engineer ISSArt QA team

Comments are closed.