Using the Selenium Webdriver

At work I'm starting a new project and decided to do something that I've only toyed with before, and that is to make automated browser testing a major part of my development lifecycle. I asked my team what they were using and they said Selenium, so I gave it a try. I also ran into some issues, so I figured I would share how to (mostly) solve those problems.

To get started, I created a new class library. In that, I created a new class called "SampleWebTests" to put my tests in. Using NuGet (pictured below), I found the Selenium Webdriver project and added it to my class library. Note that I didn't get Selenium Remote Control, which is the older version. I also added nunit (not pictured below) to use as my test framework.

Selenium webdriver on nuget

Alright, so you need to create your first test. Selenium Webdriver can be used to automate Firefox, Chrome and IE. I started with Firefox. Here's how to do a very simple test using the Firefox driver for the sample web project I created (which happens to be running on localhost port 16480). Assuming you have Firefox installed, something similar should work for you:


using System;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;

namespace WebTests
{
    [TestFixture]
    public class SampleWebTests
    {
        [Test]
        public void TestWithFirefox()
        {
            IWebDriver driver = new FirefoxDriver();
            INavigation nav = driver.Navigate();
            nav.GoToUrl("http://localhost:16480/");

            IWebElement element = driver.FindElement(By.Name("firstName"));
            string value = element.GetAttribute("value");
            Assert.AreEqual("Bob", value);
            driver.Close();
        }
    }
}

So on the test page I have an input box with the name "firstName" and it has a value of "Bob". Going line by line (counting from the beginning of the test), this test (1) creates a Firefox driver, (2) creates an INavigation object, (3) uses it to navigate to the page, (4) finds the input on the page by name, (5) gets the value, (6) makes sure it is equal to "Bob" and finally (7) closes the browser. Mostly straightforward, I think. The only question I had looking at the api was around why a separate navigation object was necessary. Regardless, you use it to navigate to a page, reload it or hit back or next in the browser. Easy enough.

Chrome

After I created my first few tests, all using Firefox, I was pretty satisfied. Then I talked to @MarkLeonWatson (who is always the troublemaker) on Twitter and he mentioned that he was having issues with both Chrome and IE. So I tried it and kablooey. Code below:


[Test]
public void TestWithChrome()
{
    //Note: does not work yet
    IWebDriver driver = new ChromeDriver();
    INavigation nav = driver.Navigate();
    nav.GoToUrl("http://localhost:16480/");

    IWebElement element = driver.FindElement(By.Name("firstName"));
    string value = element.GetAttribute("value");
    Assert.AreEqual("Bob", value);
    driver.Close();
}    

Here is my test run output from running this through TestDriven.net:


Test 'WebTests.SampleWebTests.TestWithChrome' failed: OpenQA.Selenium.WebDriverException : The file D:\Dev\Trash\SeleniumTest\WebTests\bin\Debug\chromedriver.exe does not exist.
	at OpenQA.Selenium.Chrome.ChromeDriverService.CreateDefaultService(String driverPath)
	at OpenQA.Selenium.Chrome.ChromeDriverService.CreateDefaultService()
	at OpenQA.Selenium.Chrome.ChromeDriver..ctor()
	SampleWebTests.cs(28,0): at WebTests.SampleWebTests.TestWithChrome()

0 passed, 1 failed, 0 skipped, took 0.49 seconds (NUnit 2.5.10).

Oh snap! What to do about this? Fortunately, this problem is fixable. Unfortunately, it's a pain. The resource I used to solve this is this bit on Google code. So first you have to download Chromium. Second you have to start Chromium before the you run the core of the test, then stop it afterwards. It doesn't have a "Stop()" method for .NET (though there appears to be one for Java) so I'm hoping that disposing of it will do the trick. The CreateDefaultService method takes a parameter specifying the location of the chromedriver.exe that you downloaded (if you are following along). I couldn't get a relative path to work but if I ran it with no parameter it looked in the bin/debug folder for my class library that holds my tests. Even though that is rather suboptimal, I dropped it there for now. Here is my modified test:


[Test]
public void TestWithChrome()
{
    using (var driverService = ChromeDriverService.CreateDefaultService())
    {
        driverService.Start();

        IWebDriver driver = new ChromeDriver();
        INavigation nav = driver.Navigate();
        nav.GoToUrl("http://localhost:16480/");

        IWebElement element = driver.FindElement(By.Name("firstName"));
        string value = element.GetAttribute("value");
        Assert.AreEqual("Bob", value);
        driver.Close();
    }
}

I ran it and the test passed, which is good. In the process it opened a couple console windows as well as Chrome. It managed to close the browser window but only one of the console windows when it finished, which is a bit disappointing. But it worked. Here is the console left open after the test:

Console left open after running the Chrome web driver

This whole other service thing is going to be a problem. You probably don't want to start up the server for every test you run in Chrome. I would assume that's going to be rather slow. And you wouldn't want to have that console window staying open on a build server. I ran the test again and it left yet another console window open, which is definitely not something you want happening on a build server.

Internet Explorer

So I make a copy of the Firefox test, change it to the IE driver hoping solving this problem won't be as much of a pain. Here is my test:


[Test]
public void TestWithInternetExplorer()
{
    IWebDriver driver = new InternetExplorerDriver();
    INavigation nav = driver.Navigate();
    nav.GoToUrl("http://localhost:16480/");

    IWebElement element = driver.FindElement(By.Name("firstName"));
    string value = element.GetAttribute("value");
    Assert.AreEqual("Bob", value);
    driver.Close();
}

I run it and get this error:


Test 'WebTests.SampleWebTests.TestWithInternetExplorer' failed: System.InvalidOperationException : Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones. (NoSuchDriver)
	at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
	at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(DriverCommand driverCommandToExecute, Dictionary`2 parameters)
	at OpenQA.Selenium.Remote.RemoteWebDriver.StartSession(ICapabilities desiredCapabilities)
	at OpenQA.Selenium.IE.InternetExplorerDriver..ctor()
	SampleWebTests.cs(47,0): at WebTests.SampleWebTests.TestWithInternetExplorer()

0 passed, 1 failed, 0 skipped, took 2.51 seconds (NUnit 2.5.10).

I also get an error from Windows saying that the TestDriven.net process has stopped working. Things are not looking good...

So I did what it said and turned off (I didn't try turning them all on) protected mode in IE for all zones (IE 8 in this case) and ran the test. Fortunately, the test worked (I confess that I did not expect that). The only problem I had was that I still got the Windows error saying that there was something wrong with the TestDriven.net process. This is annoying on my local box but may work fine on a build box, but I'll leave that to you to explore if you so desire. For grins I duplicated the IE test and ran both tests. I only got one process hangup.

Because I figured you might want it, here is the entire test file for your reading pleasure:


using System;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.IE;

namespace WebTests
{
    [TestFixture]
    public class SampleWebTests
    {
        [Test]
        public void TestWithFirefox()
        {
            IWebDriver driver = new FirefoxDriver();
            INavigation nav = driver.Navigate();
            nav.GoToUrl("http://localhost:16480/");

            IWebElement element = driver.FindElement(By.Name("firstName"));
            string value = element.GetAttribute("value");
            Assert.AreEqual("Bob", value);
            driver.Close();
        }

        [Test]
        public void TestWithChrome()
        {
            using (var driverService = ChromeDriverService.CreateDefaultService())
            {
                driverService.Start();

                IWebDriver driver = new ChromeDriver();
                INavigation nav = driver.Navigate();
                nav.GoToUrl("http://localhost:16480/");

                IWebElement element = driver.FindElement(By.Name("firstName"));
                string value = element.GetAttribute("value");
                Assert.AreEqual("Bob", value);
                driver.Close();
            }
        }

        [Test]
        public void TestWithInternetExplorer()
        {
            IWebDriver driver = new InternetExplorerDriver();
            INavigation nav = driver.Navigate();
            nav.GoToUrl("http://localhost:16480/");

            IWebElement element = driver.FindElement(By.Name("firstName"));
            string value = element.GetAttribute("value");
            Assert.AreEqual("Bob", value);
            driver.Close();
        }
    }
}

Conclusion

I like Selenium Webdriver for Firefox. That was quite pleasant. Working with it with Chrome (especially) and IE has some challenges. Perhaps you don't mind, dear reader. Either way, good luck! Hope you found this helpful.

Comments

@MarkLeonWatson 2011-09-30 10:19:42

I approve this post :) Nice to see it working, even with the hoops to jump through.

David Nelson 2011-09-30 03:11:06

"Perhaps you don't mind, dear reader."

You've been reading too much Hanselman :)

Eric 2011-10-04 07:57:54

@David Believe it or not, I actually don't read Hanselman all that much but it probably rubbed off anyway. I never miss his podcasts though.

tmack 2011-10-04 10:08:46

I ran into the problem of chromedriver not closing its console as well. Try:


driver.Quit();


instead of


driver.Close();

Stephen 2011-10-09 10:09:42

I've just tried out the ChromeDriver and you don't need to start up a service instance (it's done natively in the background with the use of the ChromeDriver object) so long as you have a copy of the chromedriver.exe in the bin folder, or if you use the constructor with a path to the folder (don't put the file name in there) containing the chromedriver.exe

If you do want to run the chromedriver.exe via code (like in your example), you have to use the RemoteWebDriver instead which will mean you only end up with one instance of ChromeDriver.exe running, and will close when you dispose of the service. I'll happily post the source code if this doesn't make sense.

Thanks for your post, it helped me sort out figuring out where I was going off-track.

hsenaj 2012-06-07 06:00:17

Thanks. Great article

ed sykes 2012-08-07 08:15:46

You can put chromedriver.exe and reference the folder path in the creation of the ChromeDriverService object:


ChromeDriverService.CreateDefaultService(@"c:\chromedriver");

You can use a relative path from bin/debug, which is the working directory

Note: specify the path of the folder rather than the exe.

TD 2013-04-11 01:38:46

WebsiteUrl:

This can quickly add up if you want all your test to run on all three browsers. Do you know a simpler way to specify the browser for each test?