masaj salonu masaj salonları
Home » Advertising » Finish Him: Kill All the WebDriver C# Code

Finish Him: Kill All the WebDriver C# Code

Most of my articles are about WebDriver. Today, I am going to write about a common problem that people usually hit. Have you experienced this? Your tests finish and then all of a sudden, the browser is still opened? The next time you try to clean the build folder, you cannot because the current driver’s EXE is still in use. Such a pity. From now on, your builds start failing. Many people complain about the flakiness of WebDriver, and this is one of the reasons. Here, I am going to propose to you a solution: As they say in Mortal Kombat, “Finish Him!” (kill all of the processes).

Test C# Code

Once again, I will use one of my favorite test pages: Bing. Below, you can find the page object that we will use in the tests.

BingMainPage.Actions

public partial class BingMainPage {
	private readonly IWebDriver _driver;
	private readonly string _url = @"http://www.bing.com/";
	public BingMainPage(IWebDriver browser) = _driver = browser;
	public void Navigate() = _driver.Navigate().GoToUrl(_url);
	public void Search(string textToType) {
		SearchBox = textToType;
		GoButton.Click();
	}
}

BingMainPage.Elements

public partial class BingMainPage {
	public IWebElement GoButton = _driver.FindElement(By.Id("sb_form_go"));
	public IWebElement ResultsCountDiv = _driver.FindElement(By.Id("b_tween"));
	public string SearchBox {
		get = _driver.FindElement(By.Id("sb_form_q")).Text;
		set = _driver.FindElement(By.Id("sb_form_q")).SendKeys(value);
	}
}

BingMainPage.Asserts

public partial class BingMainPage {
	public void AssertResultsCount(string expectedCount) = Assert.AreEqual(ResultsCountDiv.Text, expectedCount);
}

We use partial classes. You can read more about this modified/improved version of the pattern in my article Page Objects with Partial Classes and Properties: WebDriver C#.

Partial Page Objects in Tests

Nothing special, really. We initialize the driver for each test and then perform the test case. By the way, we can reuse the browser if it is started in the assembly initialize method. This way, the test run’s speed will be significantly improved.

[TestClass]
public class BingTests {
	private IWebDriver _driver;
	[TestInitialize]
	public void TestInit() {
		_driver = new ChromeDriver();
		_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
	} [TestCleanup]
	public void TestCleanup() {
		_driver.Quit();
	} [TestMethod]
	public void SearchTextInBing_First() {
		var bingMainPage = new BingMainPage(_driver);
		bingMainPage.Navigate();
		bingMainPage.Search("Automate The Planet");
		bingMainPage.AssertResultsCount("236,000 RESULTS");
	}
}

However, imagine that we have instead of a single test, we have 2,000 tests — or even more. Yeah. I bet that you will hit the problem I mentioned earlier. How often you experience it depends on the driver type you use. Some are more stable than others. For example, I noticed that it occurs more often for InternetExplorerDriver and OperaDriver.

The Solution: Kill Them All

How to Use it in Tests?

[TestClass]
public class BingTests {
	private static IWebDriver _driver;
	[AssemblyInitialize]
	public static void AssemblyInitialize(TestContext testContext) {
		DisposeDriverService.TestRunStartTime = DateTime.Now;
		_driver = new ChromeDriver();
		_driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(30);
	} [AssemblyCleanup]
	public static void AssemblyCleanUp() {
		DisposeDriverService.FinishHim(_driver);
	} [TestMethod]
	public void SearchTextInBing_First() {
		var bingMainPage = new BingMainPage(_driver);
		bingMainPage.Navigate();
		bingMainPage.Search("Automate The Planet");
		bingMainPage.AssertResultsCount("236,000 RESULTS");
	}
}

Above, we reuse the driver instance for all tests in the current test run. Finally, when all of them finish, we finish the driver for good. Below, you will find how the magic works.

DisposeDriverService: Finish Him

public static class DisposeDriverService {
	private static readonly List  string  _processesToCheck = new List  string  {
		"opera",
		"chrome",
		"firefox",
		"ie",
		"gecko",
		"phantomjs",
		"edge",
		"microsoftwebdriver",
		"webdriver"
	};
	public static DateTime ? TestRunStartTime {
		get;
		set;
	}
	public static void FinishHim(IWebDriver driver) {
		driver ? .Dispose();
		var processes = Process.GetProcesses();
		foreach(var process in processes) {
			try {
				Debug.WriteLine(process.ProcessName);
				if (process.StartTime  TestRunStartTime) {
					var shouldKill = false;
					foreach(var processName in _processesToCheck) {
						if (process.ProcessName.ToLower().Contains(processName)) {
							shouldKill = true;
							break;
						}
					}
					if (shouldKill) {
						process.Kill();
					}
				}
			}
			catch(Exception e) {
				Debug.WriteLine(e);
			}
		}
	}
}

Our first job is to set the TestRunStartTime before the test execution. As you can find out above, we do that in the AssemblyInitialize method before any test is executed. Then we call the FinishHim method in the AssemblyCleanup after all tests. First, we give a chance of the current driver to dispose of itself. If any browsers or driver’s processes are still present after that, we kill them.

But why do we need to set the TestRunStartTime then? Because you don’t want to kill any browsers that weren’t started from your tests. Because of that, for each process, we check its start time. If it was created after the test run started, we may potentially kill it. The second validation that we make is to find out if the process is a browser or driver. If it is, we kill it.

Download the full source code here.

Leave a Reply

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

*
*

cover letter