Verifying with Selenium

You’re testing a desktop application with Selenium. How do you verify an item on the page after the browser launches?

I recently came up against this use case and couldn’t find a clear answer for C#. Every once in a great while, the need arises to have Selenium communicate to a browser that is already launched, sitting at some page. While the bulk of examples available involve launching the browser with Selenium and then attaching with a different driver, I couldn’t find a way to verify something on the page if Selenium didn’t launch the browser.

Sure, I found a Python and Java example here. There are also a couple of other Java tutorials floating around for how to do this a bit differently via the Canary (and newer) builds of Chrome, but I wanted to bring this goodness to the C# world for current capabilities. In the spirit of DevOps focusing on bringing teams together, both testers and developers could benefit from this. The working example that I’m providing is for testers wanting to test these sorts of scenarios as well as developers so that they have the information they need to launch with settings that will make this easier on their testers.

Let’s Get Started!

The repo I’m working out of is here.

Cutting right to the chase, the two vital pieces of code are leveraging Chrome’s remote debugging capabilities. Launching Chrome out of a WPF application looks like this:

MainWindow:

private void LaunchBrowser_Click(object sender, RoutedEventArgs e)
{
      Process proc = new Process();
      proc.StartInfo.FileName = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
      proc.StartInfo.Arguments = "https://www.intellitect.com/blog/ --new-window --remote-debugging-port=9222 --user-data-dir=C:\\Temp";
      proc.Start();
}

The highlighted portion is critical. It tells Chrome which port to use for remote debugging and where to find the relevant user data.

PageUnderTest.cs

private IWebDriver Driver { get; set; }

// Some basic Selenium calls we’ll use in our tests
public void AttachToChrome()
{
    ChromeOptions options = new ChromeOptions();
    options.DebuggerAddress = "127.0.0.1:9222";

    // Using Polly library: https://github.com/App-vNext/Polly
    // Polly probably isn't needed in a single scenario like this, but can be useful in a broader automation project
    // Once we attach to Chrome with Selenium, use a WebDriverWait implementation
    var policy = Policy
      .Handle<InvalidOperationException>()
      .WaitAndRetry(10, t => TimeSpan.FromSeconds(1));

    policy.Execute(() => 
    {
        Driver = new ChromeDriver(options);
    });
}

We point Selenium at a debugger address (port included). Now, we can “attach” to the Chrome instance launched by our desktop app.

You can now drive the Chrome instance started by our small WPF app:

// AttachToChromeTest.cs
[TestMethod]
public void LaunchChromeAndAttach()
{
    // Open WPF application, make sure a button is present, then click it to launch Chrome
    Assert.IsTrue(Window.LaunchBrowserButton.Displayed, 
        "Expected button never appears.");
    Window.LaunchBrowserButton.Click();

    // Attach to new Chrome instance
    Page.AttachToChrome();

    // Verify Chrome launched to the correct page
    Assert.AreEqual("https://intellitect.com/blog/", Page.Driver.Url);
    Assert.IsTrue(Page.BlogList.Displayed);
    Assert.IsTrue(Page.BlogHeadings.Count > 0);
}
private Window Window => new Window();
private PageUnderTest Page { get; } = new PageUnderTest();

Want More?

Check out my blog, The Feasibility of Test Automation, and discover the benefits of adding simple test automations to the early stages of your project.

Got Some Other Tricky Use Cases?

Comment below. Let’s solve them together!

Join the Conversation

  1. Mike Curn

30 Comments

Your email address will not be published. Required fields are marked *

  1. Hi thanks a lot for your work !!!
    I just have a question.
    i can know attach my program to an existing browser instance with your method but when i want to find a element to control it, nothing happened and i don’t know why.
    simple exemple :
    IWebElement screenButton = Driver.FindElement(By.iD(“Screen”));
    if (screenButton.Displayed)
    screenButton.Click();
    else
    Console.Write(“screenButton doesn’t exist”);

    Do you have an idea ?
    Thanks

    1. Hi Mins, do you get a particular exception or error with your code? I’m wondering specifically what line it fails on, and what the failure was (if any) to help determine if Selenium is trying act too fast, or if an element is in an unexpected state.

  2. Hi,

    Maybe this will help someone like me. It took me 3 days to find this blog to sort out this. I did little tweaks because I still don’t know much about C#.

    Download latest chromerdriver.exe and place it for example in C:\WebDriver. I couldn’t find the latest chromedriver.exe for chrome 87. The one available at Nuget in VS2019 was not compatible with Chrome 87.

    Now run cmd and run:
    chrome.exe -remote-debugging-port=9222 –user-data-dir=C:\Selenium_Chrome_debug\Chrome

    –user-data-dir was manually created in C:\

    Now go to VS2019 in your C# project and place the below in Main:
    ChromeOptions options = new ChromeOptions();
    options.DebuggerAddress = “127.0.0.1:9222″;
    IWebDriver driver = new ChromeDriver(@”C:\\WebDriver\\”, options);

    Now keep the chrome opened (the chrome we started with cmd) and go to the webpage you were trying to attach and run the debugger with F5.

    This works for me.

    Regards
    Sibtain.

    1. Thank you, Sibtain, that is also a great way to do it!

      Just a quick note, if you can’t find the latest chromedriver version in NuGet, it is usually posted pretty quickly on the official website:
      https://chromedriver.chromium.org/downloads

      Just be aware, if you go the route of downloading the .exe directly, you’ll need to tell visual studio to copy the .exe to your output directory after adding it to your solution or project.

      Thanks again,
      Mike Curn

  3. Mike,

    Do you by chance know if this could work in a hybrid windows application? Perhaps if the browser control in the application could have remote debugging available, selenium could still attach to it? It seems that Appium supports hybrid for iOS/Android and supports windows apps via WinAppDriver, but probably not hybrid directly in windows.

    1. Hi Shawn, that’s an excellent question. I’m really not sure. I would expect that as long as there is a Chrome process running, it should work. Do you have an example already built that could be tested?

  4. Hi Mike
    I’m not real familiar with C# so this may be a dumb question but where in your code are you getting the right version of chromedriver.exe to match the version of Chrome I have installed?

    1. Hi Mark,
      Not a dumb question at all! There’s two prevailing ways I’ve seen. The more prevalent seems to be to use a NuGet package like: https://www.nuget.org/packages/Selenium.Chrome.WebDriver

      Basically, it takes the current ChromeDriver and brings it into your project with the setting to copy it to your output folder on build. This works fine, but if you need to manage multiple versions (which is rare: usually you can run with the latest and be fine,) I find it a bit cumbersome. It’s also kind of an odd use of a package since there’s no real dependencies on the driver itself, nothing to install, etc.

      Second, and my preferred method lately, is to download the .exe directly:
      https://sites.google.com/a/chromium.org/chromedriver/

      Then you can place it wherever you want as long as you point Selenium to it when you create your WebDriver object. I tend to add it to my project, which has the added benefit of making it visible for all to see without having to explore the package manager, and set it to copy to output on build. Another benefit by downloading it directly is it’s easier to put it somewhere else entirely. I’ve seen examples (although have no personal experience with this,) of people putting it on a shared drive and having environment variables, build configs, or script variables point to it so that everyone stays on the same version.

      Hope that helps!

      Mike Curn

  5. Hi Mike
    I am trying to bind onmouseover,onmouseout,onclick events in c# for iframe elements.
    Could you please help me on this?
    Thanks in advance.

    1. Hi DS,
      The easiest way is probably combining the Driver.SwitchTo().Frame() method, and the Actions library.

      We wrote a small wrapper to handle things like this; to see how we do it at clients, check out the SwitchToIFrame() method here:
      https://github.com/IntelliTect/TestTools/blob/master/IntelliTect.TestTools.Selenate/IntelliTect.TestTools.Selenate/DriverHandler.cs

      In short, you find the iFrame element, then hand it into the Driver.SwitchTo().Frame() method (nested iFrames will require multiple Find, SwitchTo, Find, SwitchTo, etc.)

      For mouse over, clicks, etc., the Actions class has a lot of that built in. For example, you could do something like:
      var clickAndDrag = new Actions(Driver);
      clickAndDrag
      .MoveToElement(someIWebElement)
      .ClickAndHold()
      .MoveToElement(someOtherIWebElement)
      .Release()
      .Perform();

      Hope that helps!

      1. Hi Mike
        Thank you very much for your reply.
        Mike,I need to attach onmousedown, onmouseover events to aN iframe elements. So that when I click on any where in iframe I can implement my functionality in C# on this onclick event.
        Please suggest.
        Thanks in advance.

        1. Hi DS,
          Implementing that on the iframe itself is a little beyond my skills. I’ll ask around at the IntelliTect office and see if I can get someone to offer a good response!

        2. Hi DS,
          Unfortunately, I haven’t been able to find a good answer for this. The general standpoint seems to be “if you have control of the code, try not to use iframes and bind to the control how you normally would with your chosen frontend framework.” If you don’t have control of your code… I heard a lot of different possibilities, but no one was sure it’d actually work. Sorry I don’t have a better answer for you!

  6. Hi Thanks for providing the solution, But could you please help me to know how to launch Chrome always with remote-debugging-port -9222? I have tried to set the registry for chrome but that approach didn’t work.

    1. Hi Sameer,
      Great question. I’m actually not 100%. Is this the registry key you modified:
      Computer\HKEY_CLASSES_ROOT\ChromeHTML\shell\open\command

      That’s the only option I found online so far. I’ll do some more research and try to do some testing to see if I can figure something out.

    2. Hi Sameer,
      Just following up: the registry key didn’t work well for me either. I’ve looked back into this off and on over the past couple weeks and haven’t found any good information online. I’ll need to do some more testing to see if I can find anything helpful here.

    1. For this specific use case, there will be a new browser per test, yes. However, the important part of this exercise is using remote debugging tools for Chrome, with theoretically you could launch on instance of and then run multiple tests against.

      1. I have switched this to VB.net

        What i wanted to know is

        How i can add code to connect to HTTP proxy inside the browser from the C# ?

        Dim url As String = “https://www.ipleak.net/”

        Dim driver As IWebDriver
        Dim options As ChromeOptions = New ChromeOptions()
        options.DebuggerAddress = “localhost:9222”
        driver = New ChromeDriver(options)
        driver.Navigate().GoToUrl(url)

        1. Hi Uoni,
          Just to make sure I understand: do you mean connect through a proxy server to access the internet with Chrome through that server?

          1. Hey mike,

            Yes I want to make the browser connect to proxy automatic using this server options.DebuggerAddress = “localhost:9222”

            also how it will be to make it remove the cookies eachtime i open the browser

          2. If you’re launching chrome via Selenium normally, it should clear cookies each time (by default, Selenium uses incognito, or at least equivalent settings.)

            RE: Proxy, I’m not as familiar with VB.net, so there may be a different syntax to use here, but you should be able to do something like:
            options.Proxy = new Proxy()
            options.Proxy.HttpProxy = “http://someproxyurlhere/” // if you want a different proxy url than localhost:9222

            Does that help?

        1. Hi Uoni,
          Sorry for the delay. I ended up needing to grab your code and do a bit of testing. It looks like (assuming you’re on Windows,) Chrome is taking proxy settings from Window’s proxy settings. I’ll need to do a bit more research to see if this is true and how to get around it. Hopefully I’ll have something more for you next week!

        2. Hi again Uoni,
          Sorry for the delay. I did some poking around online but so far have only been able to find references to changing Windows’s proxy settings, not just Chrome’s:
          https://stackoverflow.com/questions/50777994/how-to-change-chrome-proxy-via-command-line-windows
          https://proxyway.com/guides/chrome-proxy-setup

          There’s even some help articles around this:
          https://social.technet.microsoft.com/Forums/windows/en-US/71e16dd7-f65a-4930-82ad-015c6021eb10/unable-to-change-proxy-settings-in-nonieedge-browsers

          I’ll keep trying to plug away at this, but unfortunately I’m not sure when I’ll find (or if I’ll find) a better answer. :(

          Thanks,
          Mike Curn

      2. What is wrong about the code..

        I’m trying to control proxy into the local server chrome via C#

        {
        Process proc = new Process();
        proc.StartInfo.FileName = @”C:\Program Files\Google\Chrome\Application\chrome.exe”;
        proc.StartInfo.Arguments = @”–remote-debugging-port=9222 –user-data-dir=C:\API\localhost”;
        proc.Start();

        string url = “https://www.ipleak.net/”;

        IWebDriver driver;
        ChromeOptions options = new ChromeOptions();
        options.DebuggerAddress = “localhost:9222”;
        options.AddArgument(“–proxy-server=8.8.8.8:8080”);
        driver = new ChromeDriver(options);
        driver.Navigate.GoToUrl(url);
        }

        1. Hi Tyson,
          Assuming you’re on Windows, it almost looks like Chrome is inheriting its proxy settings from Windows. I need to do a bit more research on this to be sure, though. Hopefully I’ll have something useful next week!

        2. Hi Tyson,
          Sorry for the delay. I looked around a bit but couldn’t find much beyond changing Windows’s proxy settings:
          https://stackoverflow.com/questions/50777994/how-to-change-chrome-proxy-via-command-line-windows
          https://proxyway.com/guides/chrome-proxy-setup

          I did find some help articles around this, so it doesn’t appear to be an isolated concern:
          https://social.technet.microsoft.com/Forums/windows/en-US/71e16dd7-f65a-4930-82ad-015c6021eb10/unable-to-change-proxy-settings-in-nonieedge-browsers

          I’ll keep trying to find some answers. Sorry I haven’t been more helpful on this!

          Thanks,
          Mike Curn