How do I test multiple browsers with selenium and a single NUnit suite and keep it DRY? How do I test multiple browsers with selenium and a single NUnit suite and keep it DRY? selenium selenium

How do I test multiple browsers with selenium and a single NUnit suite and keep it DRY?


NUnit 2.5+ supports Generic Test Fixtures which make testing in multiple browsers very straightforward.http://www.nunit.org/index.php?p=testFixture&r=2.5

Building the following will create two "GoogleTest" NUnit tests, one for Firefox and one for IE.

using NUnit.Framework;using OpenQA.Selenium;using OpenQA.Selenium.Firefox;using OpenQA.Selenium.IE;using System.Threading;namespace SeleniumTests {    [TestFixture(typeof(FirefoxDriver))]    [TestFixture(typeof(InternetExplorerDriver))]    public class TestWithMultipleBrowsers<TWebDriver> where TWebDriver : IWebDriver, new()    {        private IWebDriver driver;        [SetUp]        public void CreateDriver () {            this.driver = new TWebDriver();        }        [Test]        public void GoogleTest() {            driver.Navigate().GoToUrl("http://www.google.com/");            IWebElement query = driver.FindElement(By.Name("q"));            query.SendKeys("Bread" + Keys.Enter);            Thread.Sleep(2000);            Assert.AreEqual("bread - Google Search", driver.Title);            driver.Quit();        }    }}


Good question, plenty of people run into this issue. I'm a fan of injecting my browser into my test case using an IoC container. That lets me put all my browser configuration in a injection 'mudule'

I use the Java bindings and Guice as my IoC Container, but the principals are the same in .Net. You want a DefaultSelnium field in your class that gets injected. Your tests then use this object and dispose it when they're done. You may find you can inject it right away, or you may need to do the object creation in a setup method. A few things you should watch out for, depending on your unit testing framework:

  • Are your test classes created new for each test? JUnit creates a new instance of the test class for each test to be run. TestNG famously did away with this an reuses test class objects for each contained test. The problem with reuse is your injected DefaultSelenium instance is caried along for the ride, which could lead to problems if your tests are run in parallel, or change browser state.
  • Lazy Load your browser object If your Unit testing tool loads all the test classes right off the bat, it will try to create the browser objects up front, which is pretty resource intensive.

I'm sure you can Google for yourself better than I can, but these are some DI and NUnit links I thought looked promising.

NUnit integration tests and dependency injection

http://buildstarted.com/2010/08/24/dependency-injection-with-ninject-moq-and-unit-testing/

If you don't like DI I've heard of people using factory methods to generate their browser based on some external setup.


Here is an example unit test using a custom XUnit DataAttribute to provide the Driver to the test

using OpenQA.Selenium;using SeleniumPageObjectsPatternExample.Attributes;using SeleniumPageObjectsPatternExample.PageObjects;using Xunit;using Xunit.Extensions;public class HomepageTests {    [Theory]    [Browser(Type.Firefox)]    [Browser(Type.GoogleChrome)]    public void HomepageLinksToBlogPage(IWebDriver webDriver)    {        // arrange         var expected = "some expected value";        // act        var homepage = new HomePage(webDriver, true);        // assert        Assert.True(homepage.BlogLink.Displayed);        Assert.Equal(expected, homepage.Header.Text);    }}

Here is the custom DataAttribute

using System.Reflection;using OpenQA.Selenium;using SeleniumPageObjectsPatternExample.WebDriver;using Xunit.Extensions;public class BrowserAttribute : DataAttribute{    private IWebDriver WebDriver { get; set; }    public BrowserAttribute(Type browser)    {        this.WebDriver = WebDriverFactory.Create(browser);    }    public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest, System.Type[] parameterTypes)    {        return new[] { new[] { this.WebDriver } };    }}

Using this WebDriverFactory

using OpenQA.Selenium;using OpenQA.Selenium.Chrome;using OpenQA.Selenium.Firefox;using Type = SeleniumPageObjectsPatternExample.Attributes.Type;public class WebDriverFactory{    public static IWebDriver Create(Type browser)    {        IWebDriver webDriver;        switch (browser)        {            case Type.Firefox:                webDriver = new FirefoxDriver();                break;            case Type.GoogleChrome:                webDriver = new ChromeDriver();                break;            default:                webDriver = new ChromeDriver();                break;        }        webDriver.Manage().Window.Maximize();        return webDriver;    }}

And the browser type enum

public enum Type{    Firefox,    GoogleChrome}

I would advise you change the name of the enum from Type to something else...