This post is from the Experitest blog and has not been updated since the original publish date.
Best practices for Selenium and Appium testing with C# and NUnit
Read about the most useful best practices for Selenium and Appium testing with C# and NUnit and save time for test execution and analysis.
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 the code example click here.
It's considered a good practice to have the fewest 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, the 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 the code example click here.
In this test, we communicate with the AuthorizationPage class, which represents our target page. MakeSuccessfulAuthorization() is a method of this class, that performs successful authorization.
Naming your tests
For complex applications, there tends 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. Click here for an example.
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. Click here for an example.
It's one of the basic principles of 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.
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.
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. Here is the code example.
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)
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.
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. The code example can be found here.
Both Selenium and Appium testing can be easily launched on the SeeTest platform – a 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 the programming language and test framework, SeeTest enables you 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.
Digital Transformation is an important factor leading the way. Check out how it can affect your continuous testing with our webinar "Breaking down Barriers: Why testing in the cloud matters".