AdvanceTopicsOfSeleniumUsingC
Cross Browser Testing Custom Waits and Conditions Parallel test execution Selenium With C#

Advanced Selenium C# Guide: Leveraging Custom Waits and Dynamic Element Handling

In today’s fast-paced software development, delivering quality applications requires strong testing tools. Selenium is a widely used tool for automating web applications and ensuring they work well. While basic Selenium tasks are helpful, more complex tests need advanced techniques, especially with C#.

This blog will explore important advanced topics in Selenium with C#. We’ll cover handling dynamic elements, creating custom waits, running tests in parallel, and cross-browser testing. We’ll also look at optimizing test performance and integrating Selenium with CI/CD pipelines. These advanced methods will help improve your test efficiency and software quality.

Table of Contents

Handling Dynamic Elements in Selenium C#

Handling dynamic elements in Selenium C# is an essential skill for automating modern web applications, as many websites today include dynamic content that loads or changes after the page initially renders. Dynamic elements can be tricky to automate because they might not be available immediately or their properties (like ID or class) can change frequently. To ensure that your automation tests remain stable and reliable, you need to use specific strategies and tools to interact with these elements effectively.

Identifying Dynamic Elements

Dynamic elements on a webpage can appear due to JavaScript actions that occur at various times or in response to user actions, such as clicking a button or scrolling. For instance, a section of the page might only become visible after you scroll or click. The challenge is that the specifics of these elements, such as their ID, name, or XPath, can change every time the page loads, making it difficult for Selenium to locate and interact with them.

Strategies for Handling Dynamic Elements

Here are some methods to manage and interact with dynamic elements using Selenium C#:

1️⃣Using Explicit Waits

Explicit waits are crucial when dealing with dynamic elements. Instead of using implicit waits (which pause for a fixed time), explicit waits pause execution until a specific condition is met. This condition could be the element’s presence, visibility, or clickability.

For Click Action

public IWebElement UntilElementClickable(By locator, int duration = 15)
    {
        var localWait = new WebDriverWait(Driver, TimeSpan.FromSeconds(duration));
        var element = localWait.Until(ExpectedConditions.ElementToBeClickable(locator));
        return element;
    }

Usage of Click method

waitHelper.UntilElementClickable(element).Click();

The UntilElementClickable() method waits for a web element to become clickable before interacting with it. It takes a locator (e.g., By.Id) and an optional wait time (default is 15 seconds). The method uses WebDriverWait to keep checking if the element is both visible and enabled for interaction. Once the element is clickable, it returns the element, allowing you to perform actions like .Click(). This approach ensures the element is ready, reducing test failures caused by trying to interact with elements too soon.

2️⃣Locating Elements with Dynamic Selectors

You can create more flexible XPaths that rely on partial matching, like using contains() or starts-with() functions.

Example of dynamic Xpath using ‘contains’

 IWebElement dynamicElement = driver.FindElement(By.XPath("//button[contains(@id, 'submit')]"));

Example Code

Here’s how you can handle dynamic web elements in Selenium C# using an explicit wait and dynamic XPath:

public void clickOnElement(By locator)
{
    WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(15));
    IWebElement element = wait.Until(ExpectedConditions.ElementToBeClickable(locator));
 element.Click();
}

Usage of Dynamic locator

clickOnElement(By.XPath("//button[contains(@id, 'submit')]"));

3️⃣Handling AJAX Elements

AJAX-driven content loads asynchronously, meaning that the page does not reload, but the element may appear later. Using explicit waits or polling mechanisms ensures that the element has fully loaded before interacting with it.

4️⃣Retry Mechanism

Sometimes, despite using waits, a dynamic element may still fail to load due to network latency or other issues. A retry mechanism can help by attempting the interaction a few times before marking the test as failed.

int retryCount = 0;
while (retryCount < 3)
{
    try
    {
        driver.FindElement(By.Id("dynamicElementId")).Click();
        break;
    }
    catch (NoSuchElementException)
    {
        retryCount++;
        Thread.Sleep(1000); // Wait for a second before retrying
    }
}

5️⃣JavaScript Executor

In some cases, Selenium might not be able to interact with the dynamic element directly, especially if it’s not yet visible on the UI. You can use JavaScript to interact with such elements.

IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
js.ExecuteScript("document.getElementById('dynamicElementId').click();");

Custom Waits and Conditions

What Are Custom Waits?

Custom waits are explicitly written methods that go beyond predefined waits like implicitWait or WebDriverWait. They are designed to handle more specific scenarios where default waits might not suffice. With custom waits, you can wait for conditions such as elements being visible, clickable, having specific attributes, or any other requirement based on your testing needs.

Why Do You Need Custom Waits?

While implicit waits and explicit waits in Selenium cover a lot of ground, there are scenarios where they might not be enough:

  • Dynamic web elements that appear or change state during test execution.
  • Performance variations where load time changes based on server speed or internet connectivity.
  • Conditional checks that require more than just visibility or clickability of an element.
  • In such cases, custom waits allow more granular control over how and when elements are interacted with in your automation test suite.

Implementing Custom Waits in Selenium C#

Let’s see how you can make a special wait to deal with a particular situation in your Selenium tests.

Example: Waiting for Text to Show Up in an Element

Suppose you have an app where the text inside an element changes due to user actions or API responses. A regular wait might not work because the element is already in the page, but the text might not have changed yet. Here’s how you can make a special wait for this situation:

public void WaitForTextInElement(By locator, string expectedText, int timeoutInSeconds = 15) 
{ 
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds)); wait.Until(driver => { IWebElement element = driver.FindElement(locator); return element.Text.Contains(expectedText);
 }); 
}

In this example:

  • locator: Finds the element on the webpage.
  • expectedText: The text you want to see in the element.
  • timeoutInSeconds: The longest time you’re willing to wait for the text to appear.
  • This wait checks the text inside the specified element and waits until the expected text appears or the timeout is reached. This method is more reliable than regular waits because it waits for specific content inside the element

Example of Usage

Imagine you have a confirmation message that shows up after submitting a form. The form submission might take time depending on the server’s speed, so using a custom wait makes sure the test waits until the confirmation message appears.

By confirmationMessageLocator = By.Id("confirmationMessage"); string expectedConfirmationText = "Thank you for your submission!"; WaitForTextInElement(confirmationMessageLocator, expectedConfirmationText);

Real-World Example: Managing Forms That Change

Think about a situation where you have a form with multiple steps, and some fields only become active after the previous step is finished. Using a special wait to check if a button or input field is active is better than using standard waits. This makes sure your automation doesn’t try to interact with a part that isn’t ready, which would cause the test to fail.

By submitButton = By.Id("submitBtn");
WaitForElementToBeEnabled(submitButton);
driver.FindElement(submitButton).Click();

This method waits until the submit button is active, making sure the test only continues when the form is ready to be submitted.

Custom Conditions

A Custom Condition in Selenium refers to a user-defined condition that you can implement in your test automation scripts to wait for specific scenarios or events that are not covered by Selenium’s built-in conditions (like element visibility or clickability). Custom conditions allow you to define your own logic for waiting until a certain state is achieved, offering more control over the test execution.

Why Use Custom Conditions?

Selenium provides predefined conditions like elementToBeClickable, visibilityOfElement, and presenceOfElement, which work well in most situations. However, in complex or dynamic web applications, you might encounter scenarios where these built-in conditions aren’t sufficient. In such cases, custom conditions are helpful for:

  • Waiting for an element to have a specific attribute or text value.
  • Waiting for multiple conditions to be met at once.
  • Handling dynamic content or loading processes in web applications.

Custom Condition Example in Selenium C#

Imagine you need to wait for an element to have a particular CSS class, which shows it’s in a certain state (like active or selected). A custom condition lets you wait until this happens.

Example: Waiting for an Element with a Specific CSS Class

public void WaitForElementToHaveClass(By locator, string className, int timeoutInSeconds = 15)
{
    WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
    wait.Until(driver =>
    {
        IWebElement element = driver.FindElement(locator);
        return element.GetAttribute("class").Contains(className);
    });
}

In simpler terms, this code waits until an element has a specific CSS class, which helps you know when the element is in the desired state.

Cross-Browser Testing

Cross-browser testing involves checking your web application on multiple web browsers to make sure it works the same way in all of them. Since people use different browsers like Chrome, Firefox, Edge, and Safari to visit websites, it’s important to test your site to ensure it works well in each one.

Why Cross-Browser Testing Matters:

  • User Experience: Different browsers can display elements differently, causing inconsistent experiences for users. Cross-browser testing ensures that users have a smooth experience no matter which browser they use.
  • CSS/HTML Compatibility: Some browsers might not support certain CSS or HTML features, which can cause display problems.
  • JavaScript Behavior: JavaScript code can act differently in different browsers because of variations in how JavaScript engines work.

How to Do Cross-Browser Testing:

Selenium WebDriver works with different web browsers, and you can quickly change which one you’re using in your tests. Here’s an example of running the same test on Chrome, Firefox, and Edge using Selenium WebDriver.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Edge;

public class CrossBrowserTest
{
    public static void RunTestInBrowser(string browserType)
    {
        IWebDriver driver;

        // Switch based on the browser type
        switch (browserType.ToLower())
        {
            case "chrome":
                driver = new ChromeDriver();
                break;

            case "firefox":
                driver = new FirefoxDriver();
                break;

            case "edge":
                driver = new EdgeDriver();
                break;

            default:
                throw new ArgumentException("Unsupported browser: " + browserType);
        }

        // Run your test logic
        driver.Navigate().GoToUrl("https://www.jignect.tech");
        Console.WriteLine("Page Title: " + driver.Title);
        driver.Quit();
    }

    public static void Main()
    {
        RunTestInBrowser("chrome");
        RunTestInBrowser("firefox");
        RunTestInBrowser("edge");
    }
}
  • The code creates a function called RunTestInBrowser that takes a browser type as input.
  • Depending on the input (Chrome, Firefox, or Edge), it starts the corresponding browser using Selenium WebDriver.
  • The Navigate function then opens a particular website, and once the test is done, the browser shuts down using driver.Quit().
  • This way, you can conveniently run your tests on different browsers by just calling the function with the correct browser name.

Optimizing Test Performance and Reducing Flakiness

Improving test performance and minimizing unreliable results are important for dependable automated testing. Unreliable tests are those that sometimes succeed and sometimes fail without any changes to the code or application. This can happen due to unstable testing environments, timing problems, or changing web elements.

Ways to Improve Test Performance:

1️⃣Reduce Waiting Periods:

Unneeded waiting times can greatly slow down tests. Instead of using fixed waits (like Thread.Sleep), use dynamic waits such as Explicit Waits or Fluent Waits to make your tests quicker and more dependable.

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement element = wait.Until(ExpectedConditions.ElementIsVisible(By.Id("example")));

In this situation, the test will wait up to 10 seconds for the element to appear, but it will continue as soon as the element is visible, which speeds up the test.

2️⃣Run Tests in Parallel:

Running tests at the same time can shorten the total time needed to complete all test cases, especially for big sets of tests.

Example of Running Tests at the Same Time:

Using tools like NUnit, you can set up your tests to run at the same time, making sure that several tests are running together on different threads or in different browsers.


[Test, Parallelizable(ParallelScope.All)]
public void TestMethod()
{
    // Test logic here
}

3️⃣Use Efficient Locators:

 The way you find elements in your tests affects how fast they run. XPath locators usually take longer than ID, CSS, or Name locators. Try to use IDs or CSS selectors whenever you can.

// Use ID instead of XPath
IWebElement element = driver.FindElement(By.Id("submitButton"));

4️⃣Headless Browser Testing:

Running your tests without a browser interface can greatly reduce the time it takes to complete them, especially in continuous integration and delivery systems.

ChromeOptions options = new ChromeOptions();
options.AddArgument("headless");
IWebDriver driver = new ChromeDriver(options);

How to Reduce Flakiness

1️⃣Implement Custom Waits:

Sometimes, web parts take longer to appear, and Selenium might not find them quickly enough. Using special waiting times can help with this. You can set up conditions to wait for parts to show up, based on how your app works.

    public IWebElement WaitForElementToBeClickable(By locator, int timeoutInSeconds)
    {
        WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
        return wait.Until(ExpectedConditions.ElementToBeClickable(locator));
    }

    2️⃣Stabilize Test Environment:

    Ensure that your test environment is stable by:

      • Keep outside problems (like network troubles) from affecting your tests.
      • Use a special test area that is just like the real working area.
      • Make sure the test information is separate and doesn’t mix with other tests.

      3️⃣Retry Failed Tests:

      Sometimes, tests can be unpredictable because of outside issues. Adding a way to try the test again in your test system can help reduce the trouble from unpredictable tests.

      [Test, Retry(3)]  // Retries the test up to 3 times if it fails
      public void TestMethod()
      {
          // Test logic here
      }

      Parallel Execution of Tests

      Parallel execution in Selenium means running several tests at the same time instead of one after the other. This can greatly cut down the time needed to run many tests. It’s very useful in continuous integration (CI) setups, where fast test results are key for smooth development processes.

      Benefits of Parallel Execution:

      • Reduced Test Execution Time: Running tests at the same time helps the whole group of tests finish quicker, especially when there are many tests to do.
      • Better Resource Utilization: Doing tests in parallel makes better use of the computer’s power by running tests on different parts of the computer or different computers.
      • Scalability: With parallel testing, you can easily make your testing setup bigger by adding more computers to share the work.

      Setting Up Parallel Testing in C# with Selenium

      To run tests in parallel using Selenium in C#, you need to adjust your testing framework (such as NUnit or MSTest) to handle multiple tests at the same time.

      Example with NUnit:

      Here’s how to set up parallel testing using NUnit and Selenium:

      • Install Required NuGet Packages: Ensure your project has the NUnit, NUnit.Console, and Selenium.WebDriver packages installed.
        • Enable Parallel Testing in Test Class: NUnit offers the [Parallelizable] attribute, which lets you run tests simultaneously. You can apply this attribute to the entire class or individual methods.
        using NUnit.Framework;
        using OpenQA.Selenium;
        using OpenQA.Selenium.Chrome;
        
        [TestFixture]
        [Parallelizable(ParallelScope.All)]
        public class ParallelTest
        {
            private IWebDriver driver;
        
            [SetUp]
            public void Setup()
            {
                driver = new ChromeDriver();
            }
        
            [Test]
            public void TestMethod1()
            {
                driver.Navigate().GoToUrl("https://www.example.com");
                Assert.AreEqual("Example Domain", driver.Title);
            }
        
            [Test]
            public void TestMethod2()
            {
                driver.Navigate().GoToUrl("https://www.google.com");
                Assert.AreEqual("Google", driver.Title);
            }
        
            [TearDown]
            public void TearDown()
            {
                driver.Quit();
            }
        }
        

        In this example:

        • [Parallelizable(ParallelScope.All)] instructs NUnit to run the tests simultaneously.
        • TestMethod1 and TestMethod2 will be executed at the same time, which is faster than running them one after the other.

        Important Points for Parallel Execution

        • Thread Safety: Make sure your tests are safe to run in parallel, especially when they use shared resources like WebDriver. Each test should have its own WebDriver instance.
        • Test Independence: Tests should not rely on each other or change shared data. Each test should be separate and manage its own setup and cleanup.
        • Cross-Browser Testing: When running tests in parallel, you can set up different tests to run on different browsers (e.g., Chrome, Firefox) to check compatibility across browsers.

        Integrating Selenium Tests with CI/CD Pipelines

        Incorporating Selenium tests into Continuous Integration/Continuous Deployment (CI/CD) workflows is an important part of modern software development. This practice ensures that automated tests are run regularly during the development cycle, giving quick feedback on any changes made to the code. By doing this, it helps find issues early, enhances the overall quality of the code, and makes the release process faster.

        What is CI/CD?

        • CI (Continuous Integration) involves regularly merging code changes into a shared repository throughout the day. Each code change is automatically tested to make sure it doesn’t cause any issues with the existing features.
        • CD (Continuous Deployment) automatically releases code to the live environment, ensuring that every change goes through a series of automated tests before it is deployed.

        Incorporating Selenium tests into a CI/CD pipeline means running automated tests on every code commit or pull request, ensuring that the web application functions correctly with the latest updates.

        Why Combine Selenium with CI/CD?

        • Early Bug Catching: Running tests early in the development process helps find bugs right after new code is added.
        • Automatic Testing: Selenium automatically checks if an application works as intended, which saves time compared to testing by hand.
        • Quicker Releases: By automating tests in the pipeline, you can push out updates more quickly and with greater certainty.
        • Testing on Multiple Browsers: Selenium lets you test on various browsers within the pipeline, ensuring everything works well across different platforms.

        Example of CI/CD Integration with Selenium and Jenkins

        Let’s see how to include Selenium tests in a CI/CD process using Jenkins, a well-known tool for automation.

        • Step 1: Set Up Jenkins
          • First, install Jenkins and set it up to work with your code storage system (like GitHub, GitLab, or Bitbucket). Jenkins will watch the code storage for changes and start builds when needed.
        • Step 2: Set Up the Jenkins Job
          • Create a New Jenkins Job: Make a “Freestyle project” and connect it to your code storage.
          • Set Up Build Triggers: Tell Jenkins to start a build whenever new code is added to the storage.
          • Add Build Steps: Include steps to compile the code and run Selenium tests.

        Example configuration for a build step to execute Selenium tests using C# and NUnit:

        dotnet restore
        dotnet build
        dotnet test --filter FullyQualifiedName~YourNamespace.YourTestClass
        
        • Step 3: Run Selenium Tests in the Pipeline

        After configuring the job, whenever code is committed to the repository, Jenkins will:

        • Pull the latest code.
        • Build the project.
        • Run the Selenium tests.

        You can use the Jenkins JUnit plugin to view test results directly in the Jenkins dashboard.

        • Step 4: Handling Test Results

        Jenkins will show a summary of the test results, including how many tests passed, failed, or were skipped. If any test fails, Jenkins can be configured to:

        • Notify the team via email or Slack.
        • Stop the pipeline to prevent broken code from being deployed.
        • Automatically retry tests to eliminate flaky failures.

        C# Selenium Test Example

        Here’s a basic Selenium test written in C# that can be added to a CI/CD pipeline:

        using NUnit.Framework;
        using OpenQA.Selenium;
        using OpenQA.Selenium.Chrome;
        
        namespace SeleniumTests
        {
            public class GoogleSearchTest
            {
                IWebDriver driver;
        
                [SetUp]
                public void SetUp()
                {
                    driver = new ChromeDriver();
                    driver.Manage().Window.Maximize();
                }
        
                [Test]
                public void SearchInGoogle()
                {
                    driver.Navigate().GoToUrl("https://www.google.com");
                    IWebElement searchBox = driver.FindElement(By.Name("q"));
                    searchBox.SendKeys("Selenium CI/CD Integration");
                    searchBox.Submit();
                    
                    Assert.IsTrue(driver.Title.Contains("Selenium CI/CD Integration"));
                }
        
                [TearDown]
                public void TearDown()
                {
                    driver.Quit();
                }
            }
        }

        This test looks for “Selenium CI/CD Integration” on Google and checks if the page title includes the search term. The test can run as part of the Jenkins build process.

        • Step 5: Continuous Deployment (Optional)
          • In Continuous Deployment, after the Selenium tests are successful, the application can be automatically sent to live or testing environments. Tools like Jenkins, CircleCI, or Azure DevOps can help with this, making the whole process automatic.

        Benefits of Combining Selenium Tests with CI/CD

        • Quick Feedback: Developers receive instant feedback on code updates, allowing for faster corrections.
        • Less Manual Work: Automated tests run constantly, decreasing the need for manual checks.
        • Improved Teamwork: Teams can concentrate on development while maintaining code quality through automated tests.
        • Trustworthy Releases: By including tests in CI/CD, you lower the chance of bugs in the final product, ensuring more dependable software.

        Conclusion:

        In today’s fast-paced software development environment, QA testers are increasingly reliant on advanced automation tools like Selenium to ensure the quality and stability of web applications. While Selenium is widely used for basic automation tasks, more complex test scenarios often require a deeper understanding and the application of advanced techniques, especially when working with Selenium in C#. Therefore, this blog delves into key advanced topics such as handling dynamic elements, custom waits, parallel test execution, and cross-browser testing, all of which enhance the efficiency and accuracy of automated testing. Moreover, these techniques are crucial for testers aiming to keep up with the evolving demands of modern web applications.

        One of the significant challenges testers face is dealing with dynamic web elements that change based on user interactions or page load times. In this context, the blog discusses various strategies for handling these elements, such as using explicit waits, dynamic locators, and retry mechanisms, ensuring that automation tests are both stable and reliable. Furthermore, it highlights the importance of custom waits and conditions, allowing testers to fine-tune their automation scripts to account for specific scenarios that default waits cannot handle. In addition, the blog covers optimizing test performance by reducing unnecessary waits, running tests in parallel, and utilizing cross-browser testing to ensure consistent behavior across different platforms, all while integrating these practices into CI/CD pipelines to maintain continuous delivery and fast feedback loops.

        Witness how our meticulous approach and cutting-edge solutions elevated quality and performance to new heights. Begin your journey into the world of software testing excellence. To know more refer to Tools & Technologies & QA Services.

        If you would like to learn more about the awesome services we provide, be sure to reach out.

        Happy testing! 🙂