I\'m looking for a way to reuse one NUnit test suite without duplicating the entire suite for each browser. It seems like I would need a new fixture for each browser. Can I send
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:
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...
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();
}
}
}
[SetUp]
public void CreateDriver()
{
//driver = new TWebDriver();
if (typeof(TWebDriver).Name == "ChromeDriver")
{
ChromeOptions options = new ChromeOptions();
options.AddArguments("--incognito");
driver = new ChromeDriver(options);
}
else if (typeof(TWebDriver).Name == "FirefoxDriver")
{
FirefoxOptions options = new FirefoxOptions();
options.UseLegacyImplementation = false;
options.SetPreference("browser.private.browsing.autostart", true);
options.AddArgument("-private");
driver = new FirefoxDriver(options);
}
else if (typeof(TWebDriver).Name == "InternetExplorerDriver")
{
InternetExplorerOptions options = new InternetExplorerOptions();
options.BrowserCommandLineArguments = "-private";
options.EnsureCleanSession = true;
options.IgnoreZoomLevel = true;
options.EnablePersistentHover = true;
driver = new InternetExplorerDriver(options);
} else
driver = new TWebDriver();
}
I recommend an important variation on Lucifer's solution. Have the attribute supply a factory, not a driver, to Fact/Theory methods. Why? Well, when you see all those browser windows pop-up (n x m, where n = number of tests, m = browsers per test), you'll decide you only want them created when the test is actually going to run.
So with updates from prior solution, including some name changes...
using OpenQA.Selenium;
using SeleniumPageObjectsPatternExample.Attributes;
using SeleniumPageObjectsPatternExample.PageObjects;
using Xunit;
using Xunit.Extensions;
namespace SeleniumHelpers
{
public class HomepageTests
{
[Theory]
[WebDriver(DriverType.Firefox)]
[WebDriver(DriverType.GoogleChrome)]
public void HomepageLinksToBlogPage(WebDriverFactory factory)
{
// arrange
IWebDriver webDriver = factory.Create(); // Browser starts here.
var expected = "some expected value";
// act
var homepage = new HomePage(webDriver, true);
// assert
Assert.True(homepage.BlogLink.Displayed);
Assert.Equal(expected, homepage.Header.Text);
// Optional cleanup (better wrapped in try/finally for entire method).
webDriver.Quit();
webDriver.Dispose();
}
}
}
Updated DataAttribute... (Note, I corrected the signature of the GetData() method to work with the latest xUnit.)
using System.Reflection;
using OpenQA.Selenium;
using Xunit.Extensions;
namespace SeleniumHelpers
{
public class WebDriverAttribute : DataAttribute
{
private WebDriverFactory WebDriverFactory { get; set; }
public WebDriverAttribute(Type browser)
{
this.WebDriver = WebDriverFactory.Create(browser);
}
public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest)
{
return new[] { new[] { this.WebDriverFactory } };
}
}
}
The new WebDriverFactory
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Firefox;
namespace SeleniumHelpers
{
public enum DriverType
{
Firefox,
GoogleChrome
}
public class WebDriverFactory
{
private DriverType _driverType;
public WebDriverFactory(DriverType driverType)
{
_driverType = driverType;
}
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;
}
}
}