2
votes

We recently started to use WebDriver (in favor of Selenium 1) for performing browser tests, using the NUnit framework. Since we want to run the tests in a variety of browsers, we define drivers for each and put them in a list during fixture set up:

[TestFixtureSetUp]
public void SetupTest()
{
    // Load drivers
    Drivers = new List<IWebDriver>
            {
                new ChromeDriver(),
                ...
            };

In every single test we iterate through the list like this:

[Test]
public void SomeTest()
{
    foreach (var driver in Drivers)
    {
        driver.Navigate().GoToUrl("...");
...

It feels wrong to do this in all the test methods. The test methods should not be concerned with what driver they should work on. Ideally we would have something like this:

public void SomeTest(IWebDriver driver)
{
    driver.Navigate().GoToUrl("...");
...

One way we could solve this is by using TestCases:

[TestCase(new ChromeDriver())]
[TestCase(new FireFoxDriver())]
...

But this is a lot of duplication and shifts the problem of correct initialization of the drivers into the attributes of every single tests. Not really a gain.

Is there any way the NUnit framework can be told to execute the whole suite of tests and inject a different parameter to the individual tests in every run? Or is there any other good solution to this?

3

3 Answers

6
votes

You should be able to use the TestCaseSourceAttribute. First create a commonly accessible class that provides the collection of web drivers:

public static class WebDriverFactory
{
    public static IWebDriver[] Drivers =
    {
        new ChromeDriver(),
        new FirefoxDriver(),
        ...
    };
}

Next, implement your web driver dependent unit tests like this:

[Test, TestCaseSource(typeof(WebDriverFactory), "Drivers")]
public void SomeTest(IWebDriver driver)
{
    driver.Navigate().GoToUrl("...");
    ...
}

Optionally, to reduce typing when implementing each unit test, also define a new Attribute class that inherits from TestCaseSourceAttribute and that only implements a default constructor:

public class WebDriverSourceAttribute : TestCaseSourceAttribute
{
    public WebDriverSourceAttribute() : base(typeof(WebDriverFactory), "Drivers")
    {            
    }
}

Using the inherited WedDriverSource attribute, the unit tests can now be simplified to:

[Test, WebDriverSource]
public void SomeTest(IWebDriver driver)
{
    driver.Navigate().GoToUrl("...");
    ...
}
4
votes

There is no "best" way of doing this, however the way I accomplished this is below:

I created an Enum:

/// <summary>
/// Enum that holds references to different browsers used in testing.
/// </summary>
public enum BrowserTypeEnum
{
    /// <summary>
    /// Google Chrome.
    /// </summary>
    Chrome, 

    /// <summary>
    /// Mozilla Firefox.
    /// </summary>
    Firefox, 

    /// <summary>
    /// Internet Explorer.
    /// </summary>
    InternetExplorer
}

Called it in the TestFixture like so:

/// <summary>
/// Tests related to browsing Google
/// </summary>
[TestFixture(BrowserTypeEnum.Chrome)]
[TestFixture(BrowserTypeEnum.Firefox)]
public class GoogleTests : AbstractTestFixture
{
}

In AbstractTestFixture:

    /// <summary>
    /// Create's the browser used for this test fixture. 
    /// <para>
    /// Must always be called as part of the test fixture set up.
    /// </para>
    /// <para>
    /// It is the actual test fixture's responsibility to launch the browser. (Usually in the test fixture setup)
    /// </para>
    /// </summary>
    protected override void CreateBrowser()
    {
        switch (BrowserType)
        {
            case BrowserTypeEnum.Chrome:
                Browser = new ChromeDriver();
                break;
            case BrowserTypeEnum.Firefox:
                Browser = new FirefoxDriver();
                break;
            case BrowserTypeEnum.InternetExplorer:
                Browser = new IEDriver();
                break;
            default:
                break;
        }
    }

May not be the best solution, but I found it pretty readable. The alternative is using something like Selenium Grid, or maybe passing the type of driver into NUnit and create it directly. You've already tried this (passing the direct type of driver) and it doesn't seem to be what you are after. The only difference could be that you pass in the type of driver into the test fixture, not the actual test.

Another alternative is if you use a CI Server solution, create a configuration setting to indicate which browser to use for the test. Have the CI Driver repeat the tests three times, editing that configuration setting each time.

I do agree, it is not the actual tests responsibility to know what kind of driver they are working with, this is why I pushed that responsibility upwards into the test fixture. The way I am doing it may not be the most "elegant" way, but it is the most readable for me at least. Somebody looking at my code can easily see that this test fixture is getting repeated, and what browsers are repeating the steps.

For me, the creation of the driver must always be in the actual TestFixture (not a base test fixture). The reason is because there is a bit of logic I want to do before opening the browser - if this logic fails (in a Setup or TestFixtureSetup method), then NUnit won't run any teardown methods. So a browser window will be left open.

So to remedy this, the very last thing I do in TestFixtureSetup, before the test is ran, is called "CreateBrowser".

0
votes

You could also write your own TestCaseProvider addin for NUnit to perform this iteration over all browsers.

Then create a new attribute like this

[Test, TestOnAllBrowsers]
public void SomeTest(IWebDriver driver)
{
    driver.Navigate().GoToUrl("...");
}

to mark all tests that should run on all browsers and fill the driver variable within the TestCaseProvider addin. However this may get complicated if you already use the TestCase attribute.