Selenium Chromedriver is creating multiple instances when only one is wanted - c#

I apologize if I come across naïve, I am new to Selenium and still learning.
We are attempting to run these selenium tests on a designated machine via IIS.
When I run the code locally, everything works perfectly. When I publish it to our machine, two instances of chromedriver will show up in the taskmanager when it is only being run once. Sometimes it will close one of the instances, while other times it doesn't close either. Sometimes it will even close both instances and work just fine. I can not figure out why it's being so inconsistent.
Here is a screen shot of the task manager after starting the code.
Any suggestions on this is welcome, below is the code I am running.
private IWebDriver _driver;
[Route("/Test_SignIn")]
public IActionResult Test_SignIn(string environment = "development")
{
string response = "Failed";
try
{
string outPutDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_driver = new ChromeDriver(outPutDirectory);
if (environment == "production")
{
_driver.Navigate().GoToUrl(productionUrl);
}
else
{
_driver.Navigate().GoToUrl(developmentUrl);
}
By userNameFieldLocator = By.Id("AccountUserName");
WebDriverWait wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(userNameFieldLocator));
_waitForLoaderToFinish();
IWebElement userNameField = _driver.FindElement(userNameFieldLocator);
IWebElement passwordField = _driver.FindElement(By.Id("AccountPassword"));
IWebElement signInButton = _driver.FindElement(By.XPath("//button[contains(text(), 'Sign In')]"));
userNameField.SendKeys(_username);
passwordField.SendKeys(_password);
signInButton.Click();
By dashboardLocator = By.Id("portalBanner_EmployeeName");
wait.Until(ExpectedConditions.ElementIsVisible(dashboardLocator));
IWebElement dashboard = _driver.FindElement(dashboardLocator);
response = "Success";
}
catch (Exception ex)
{
response = ex.Message;
}
_driver.Close();
_driver.Quit();
return Json(response);
}
private string _waitForLoaderToFinish()
{
try
{
new WebDriverWait(_driver, TimeSpan.FromSeconds(30)).Until(ExpectedConditions.InvisibilityOfElementLocated(By.Id("loader-wrapper")));
return null;
}
catch (TimeoutException e)
{
return $"{e.Message}";
}
}

ChromeDriver (and all other web drivers) are "disposable" objects in .NET. They implement the IDisposable interface. Objects that implement this interface require special handling in order to clean up after they are finished. Typically you see these objects initialized in a using(...) block, similar to below:
using (IWebDriver driver = new ChromeDriver(...))
{
// Use driver
}
Reference: C# Using Statement.
I see that you are closing and quitting the browser, but you need to call _driver.Dispose(); as well. The quick and easy way to accomplish this in your code is to call Dispose() after calling Quit():
_driver.Close();
_driver.Quit();
_driver.Dispose(); // <-- this kills Chrome.exe
return Json(response);
Or modify the method to wrap the use of _driver in a using(...) statement:
[Route("/Test_SignIn")]
public IActionResult Test_SignIn(string environment = "development")
{
string response = "Failed";
try
{
string outPutDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
// Your web driver is a LOCAL variable now, beware!
using (IWebDriver driver = new ChromeDriver(outPutDirectory))
{
if (environment == "production")
{
driver.Navigate().GoToUrl(productionUrl);
}
else
{
driver.Navigate().GoToUrl(developmentUrl);
}
By userNameFieldLocator = By.Id("AccountUserName");
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementIsVisible(userNameFieldLocator));
_waitForLoaderToFinish();
IWebElement userNameField = driver.FindElement(userNameFieldLocator);
IWebElement passwordField = driver.FindElement(By.Id("AccountPassword"));
IWebElement signInButton = driver.FindElement(By.XPath("//button[contains(text(), 'Sign In')]"));
userNameField.SendKeys(_username);
passwordField.SendKeys(_password);
signInButton.Click();
By dashboardLocator = By.Id("portalBanner_EmployeeName");
wait.Until(ExpectedConditions.ElementIsVisible(dashboardLocator));
IWebElement dashboard = driver.FindElement(dashboardLocator);
response = "Success";
}
}
catch (Exception ex)
{
response = ex.Message;
}
return Json(response);
}
Please note that your web driver becomes a local variable now. It should not be a field on the class in order to prevent other methods from accessing this disposable resource.

Related

c# run selenium browsers in parallel

I'm trying to run 2 chrome browser instances with selenium webdriver library. However Even if these two browsers were initiated separately and are in separate threads, the code controls and manipulates only one browser.
The code for creating a chrome browser and perform the testing
static void test(string url)
{
ChromeDriver driver = BrowserWorker.GetChromeBrowser(false);
driver.Navigate().GoToUrl(url);
}
The "BrowserWorkser.GetChromeBrowser" function is to return a customized chrome browser instance:
public static ChromeDriver GetChromeBrowser()
{
var chromeoptions = new ChromeOptions();
chromeoptions.AddArguments("--disable-notifications");
chromeoptions.AddArguments("--no-sandbox");
chromeoptions.AddArguments("--disable-dev-shm-usage");
chromeoptions.UnhandledPromptBehavior = UnhandledPromptBehavior.Dismiss;
chromeoptions.AddArguments("--remote-debugging-port=9222");
var chromeDriverService = ChromeDriverService.CreateDefaultService(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
chromeDriverService.HideCommandPromptWindow = true;
ChromeDriver browser = null;
int nCount = 0;
while (browser == null && nCount < 3)
{
try
{
browser = new ChromeDriver(chromeDriverService, chromeoptions, TimeSpan.FromSeconds(180));
}
catch (Exception ex)
{
Console.WriteLine("Error initializing chrome browser: " + ex.Message);
}
nCount++;
}
return browser;
}
First method:
var t1 = new Thread(() => test("https://www.google.com/"));
var t2 = new Thread(() => test("https://www.bing.com/"));
t1.Start();
t2.Start();
Second method, changed the signature of "test(string url)" to "async Task" and added "await Task.Delay(1);", so the compiler does not complain:
var list = new List<Task>();
list.Add(test("https://www.google.com/"));
list.Add(test("https://www.bing.com/"));
Task.WaitAll(list.ToArray());
However neither method worked. The two separate threads/tasks controls the same browser instance, it loads google and bing, in a random order.
Please let me know if you need additional information. Thank you.
If you launch Chrome with different arguments, it launches a new instance. So in this case making --remote-debugging-port to be in different port will make the driver launch a new instance instead of using the existing instance.

Cleaning up webdriver only in thread running without closing all drivers running in other threads

I have automated regression tests that run every morning. Currently, it launches dozens of threads simultaneously, each running its own webdriver in each thread.
ChromeOptions option = new ChromeOptions();
option.AddArgument("--headless");
IWebDriver driver = new ChromeDriver(option);
try
{
SuiteDriver(driver, suiteTable);
LogMonitor.UEErrorHandling();
}
catch (Exception ex)
{
WritetoLogFile("Exception in Main - " + ex);
}
finally
{
workbook.Dispose();
driver.Quit();
}
When the tests complete there are a bunch of webdriver instances still running. When I attempt to clean these up at the end of the test run using driver.Quit() it closes more than just the driver in its own thread, causing the other tests to fail to complete. Driver.Quit() doesn't seem to differentiate between the driver launched by this one instance and other drivers launched by other instances of the test.
Is there a way to ensure driver.Quit() or driver.Close() only closes the instance of webdriver launched by that specific executable running in that thread only?
Below is an example of thread-safe execution in C# using System.Threading.ThreadLocal
ThreadLocal<IWebDriver> DriversThread = new ThreadLocal<IWebDriver>();
IWebDriver Driver
{
set => DriversThread.Value = value;
get => DriversThread.Value;
}
string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); // Pass this directory path to chromedriver when you are installing chromedriver through Nuget packages.
ChromeOptions chromeOpt = new ChromeOptions();
chromeOpt.AddArguments("--headless");
Driver = new ChromeDriver(directoryPath, chromeOpt);
Driver.Url = "https://google.co.in";
Driver.Quit();
This will set thread specific drivers, each thread will have its own driver no matter how many threads are being executed.
Try and give each webdriver it's own profile, and if needed download path.
private ChromeOptions GetChromeOptions()
{
var options = new ChromeOptions();
ProfilePath = Path.Combine(AppContext.BaseDirectory, "tmp", Guid.NewGuid().ToString());
DownloadPath = Path.Combine(ProfilePath, "Downloads");
if (!Directory.Exists(DownloadPath))
{
Directory.CreateDirectory(DownloadPath);
}
options.AddUserProfilePreference("download.default_directory", DownloadPath);
//--lang=en-US,en headless does not define a language by default
options.AddArguments("--incognito", "--lang=en-US,en", $#"--user-data-dir={ProfilePath}");
return options;
}
I'm using xUnit to spawn dozens of headless chromes tests and I've not ever seen the dispose close all of the instances. The only difference I can think of is that I am spawning each with their own profile.
I would suggest using the setup and teardown of your testing framework onto a base class to handle the setup and teardown for all tests.
public class PageLoad: IDisposable
{
private IWebDriver _driver;
public void PageLoad()
{
_driver = new ChromeDriver();
}
[Fact]
public void PageLoadTest()
{
_driver.Navigate().GoToUrl("http://google.com");
Assert.True(_driver.FindElement(By.XPath("//*[#id='hplogo']")).Displayed);
}
public void Dispose()
{
_driver.Dispose();
}
}
You can also wrap the driver in a using statement
using (var driver = new ChromeDriver())
{
driver.Navigate().GoToUrl("http://google.com");
Assert.True(_driver.FindElement(By.XPath("//*[#id='hplogo']")).Displayed);
}

Webrequest cant "escape" timeout

Hi everyone im trying to create a method that will always return a url source, if for example internet goes off it will continue working until it comes up and return the url source and so on if something else occurs. So far in my method when i "turn off" the internet and "turn it on" back procedures continue normaly but im having an issue when a timeout occurs and im "falling" in a loop i know that the while(true) is not the right approach but im using it for my tests.
So how can i skip the timeout exception and "retry" my method?
public static async Task<string> GetUrlSource(string url)
{
string source = "";
while (true)
{
HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(url);
hwr.AllowAutoRedirect = true;
hwr.UserAgent = UserAgent;
hwr.Headers.Add(hd_ac_lang[0], hd_ac_lang[1]);
hwr.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
hwr.Timeout = 14000;
try
{
using (var response = hwr.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
source = await reader.ReadToEndAsync();
if (check_source(source))
{
return source;
}
}
}
}
catch (WebException ex)
{
hwr.Abort();
if (ex.Status == WebExceptionStatus.ProtocolError)
{
if (((HttpWebResponse)ex.Response).StatusCode == HttatusCode.NotFound)
{
// handle the 404 here
return "404";
}
}
else
{
Console.WriteLine(ex.Status.ToString());
}
}
}
}
Note: i used to have the hwr.Abort(); into a finnaly clause but it didnt help.
Edit: the console is writting this message every 14 seconds as my timeout i think its something related with that.
ِAn alternative solution to get rid of timeout problem can be is to use WebBrowser component and to navigate to the required url(webbrowser1.Navigate(url);) ,and to wait in a loop until the documentcompleted event is raised and then to get the source code by this line :
string source = webbrowser1.DocumentText;
Well it seems that i found a solution that was related with the service point of the request.
So in my catch when a timeout occurs im using this to release the connection.
hwr.ServicePoint.CloseConnectionGroup(hwr.ConnectionGroupName);
I'll keep this updated.

Selenium 2.35 WebDriver Quit Fails with IE 10

I open a page and at the end of the test attempt to close it by calling my CelanUp() in a finally block but sometimes IEDriverServer and the browser are left open. There are no open popups when this happens.
public void CelanUp ()
{
string dialogResponse = string.Empty;
if (m_Driver != null)
{
//Dismiss all dialogs:
dialogResponse = CloseLogonDialog();
dialogResponse = CloseConfirmDialog();
m_Driver.Quit();
m_Driver = null;
}
if (dialogResponse != string.Empty)
{
throw new ApplicationException(dialogResponse);
}
}
What else can I do the close the browser and IEDriverServer?
Edit:
because Selenium's Quit is letting me down by leaving windows open i have looked at other solutions:
try
{
foreach (Process proc in Process.GetProcessesByName("IEDriverServer.exe"))
{
proc.Kill();
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
Depending on what unit testing software you're using, instead of doing the logic like this, i'd recommend delegating the browser to the unit testing framework.
Put the cleanup method in an After method. as in, After each test... do this.
In java, using jUnit, it'd be written like this.
#After
public void cleanUp() {
driver.quit();
}
This means, after each test, call driver.quit()

Selenium RC Reminiscing the "End of the World"

To explain the title.. Selenium RC keeps insisting that
A system shutdown has already been scheduled
and refusing to conduct automated tests because of this.. I can understand the logic here, I mean you're not going to do your homework if you thought the world would end with a nuclear holocaust...
However.. this is not the Cold War and when I inspect several basic things (such as using shutdown \a, completing a full restart) I find that this is actually not the case!
How can I convince selenium that the world is not going to end and that it should probably do the work I'm telling it to?
N.B. Here, selenium has "refused" to initialise any instance of IE, and will continue to hang until it times out, regardless of clicking Yes or No. I'm using NUnit, c#/.net4.0 to control the tests.
To fix this I replaced the default "runSeleniumTest" function with the below patched version as a user extension:
function runSeleniumTest() {
runOptions = new RemoteRunnerOptions();
var testAppWindow;
if (runOptions.isMultiWindowMode()) {
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
} else if (sel$('selenium_myiframe') != null) {
var myiframe = sel$('selenium_myiframe');
if (myiframe) {
testAppWindow = myiframe.contentWindow;
}
}
else {
proxyInjectionMode = true;
testAppWindow = window;
}
selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode);
if (runOptions.getBaseUrl()) {
selenium.browserbot.baseUrl = runOptions.getBaseUrl();
}
if (!debugMode) {
debugMode = runOptions.isDebugMode();
}
if (proxyInjectionMode) {
LOG.logHook = logToRc;
selenium.browserbot._modifyWindow(testAppWindow);
}
else if (debugMode) {
LOG.logHook = logToRc;
}
window.selenium = selenium;
commandFactory = new CommandHandlerFactory();
commandFactory.registerAll(selenium);
currentTest = new RemoteRunner(commandFactory);
var doContinue = runOptions.getContinue();
if (doContinue != null) postResult = "OK";
currentTest.start();
}
I found that the "a system shutdown has already been scheduled" error occurred inside of "openSeparateApplicationWindow". I also found that refreshing the selenium test runner window after the error occurred would "restart" the test without the error. Therefore, I patched the "runSeleniumTest" with the following try catch statement so the test runner window reloads if there's an error in "openSeparateApplicationWindow":
try{
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
}
catch (e) {
window.onunload = function () { };
window.location.reload();
return;
}
I also used my blog post for a more specific example of selenium user extensions
Selenium isn't doing anything in this case. That's the IE HTA agent (a built-in Windows process) that's preventing you from doing anything. Perhaps rebooting the machine will do the trick? It looks like you may just have a pending Windows update that's scheduled a future reboot.

Categories

Resources