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

前端 未结 5 1324
眼角桃花
眼角桃花 2021-02-14 17:51

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

相关标签:
5条回答
  • 2021-02-14 17:57

    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...

    0 讨论(0)
  • 2021-02-14 18:09

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-14 18:10
    [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();
        }
    
    0 讨论(0)
  • 2021-02-14 18:14

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-14 18:16

    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.

    0 讨论(0)
提交回复
热议问题