从C# NUnit逐个在多个浏览器中运行Selenium测试

30

我正在寻找推荐的/最好的方法,以便使Selenium测试在多个浏览器上一个接一个地执行。我测试的网站不大,因此目前不需要并行解决方案。

我具有通常的测试设置方法,包括[SetUp][TearDown][Test]。当然,SetUp方法实例化一个新的ISelenium对象,并使用我想要测试的任何浏览器。

所以我的想法是编程方式地说明:这个测试将按顺序在Chrome、IE和Firefox上运行。我该怎么做呢?

编辑:

这可能会有所帮助。我们正在使用CruiseControl.NET在成功构建后启动NUnit测试。有没有办法向NUnit可执行文件传递参数,然后在测试中使用该参数?这样,我们可以使用不同的浏览器参数多次运行NUnit。

8个回答

50

NUnit 2.5+现在支持通用测试夹具,该夹具使在多个浏览器中进行测试非常简单。 http://www.nunit.org/index.php?p=testFixture&r=2.5

运行以下示例将在Firefox和IE中各执行一次GoogleTest。

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();
        }
    }
}

@alanning 我的系统无法识别IE驱动程序的位置,我需要将它放在哪里才能运行? - DEnumber50
1
@DEnumber50 好久没做这个了,但是https://code.google.com/p/selenium/wiki/InternetExplorerDriver 上说可执行文件必须在你的路径中。此外,注意如果你使用IE11,则需要配置。 - alanning
还可以参考@arve-systad在下面的答案中指定驱动程序位置的示例。 - alanning
有没有一种方法可以继承这个类,以避免每个测试类中的冗余代码? - bit

6
这是一个经常出现的问题,有几种解决方法:
  1. 工厂方法生成您的ISelenium对象 - 您可以使用带有静态getSelenium方法的助手类。该方法读取一些外部配置,其中包含一个以字符串形式定义所需浏览器的属性。在getSelenium中,您可以相应地配置浏览器。以下是有关使用NUnit配置文件的有用文章http://blog.coryfoy.com/2005/08/nunit-app-config-files-its-all-about-the-nunit-file/

  2. 其他人通过IoC容器注入浏览器也取得了成功。我非常喜欢这种方法,因为TestNG在Java领域中与Guice非常配合,但我不确定如何轻松地混合使用NUnit和Ninject、MEF等...


我有辅助工具和可配置的浏览器字符串,但问题是我必须使用不同的浏览器多次运行相同的测试。 - Edgar
然后,您应该在每次运行时交换配置文件。整个配置文件将是相同的,但您需要为要测试的每个浏览器设置一个字符串作为浏览器名称。 - pnewhook

5

这基本上只是对alanning的答案(2011年10月21日20:20)的扩展。我的情况类似,只是我不想使用无参数的构造函数(因此使用默认路径到驱动程序可执行文件)。我有一个包含我想要测试的驱动程序的单独文件夹,这似乎很好地解决了问题:

[TestFixture(typeof(ChromeDriver))]
[TestFixture(typeof(InternetExplorerDriver))]
public class BrowserTests<TWebDriver> where TWebDriver : IWebDriver, new()
{
    private IWebDriver _webDriver;

    [SetUp]
    public void SetUp()
    {
        string driversPath = Environment.CurrentDirectory + @"\..\..\..\WebDrivers\";

        _webDriver = Activator.CreateInstance(typeof (TWebDriver), new object[] { driversPath }) as IWebDriver;
    }

    [TearDown]
    public void TearDown()
    {
        _webDriver.Dispose(); // Actively dispose it, doesn't seem to do so itself
    }

    [Test]
    public void Tests()
    {
        //TestCode
    }
}

}


+1 太棒了 - 这样更整洁,你只需要用不同的驱动程序装饰你的类。 - Deano
有没有一种方法可以继承这个类,以避免每个测试类中的冗余代码? - bit
我不知道 TestFixture 属性在继承中的工作原理,但是 SetUp 和 TearDown 内部的所有内容都可以移动到其他类中,并让测试只调用 "GetWebDriver" 或其他函数。我猜你可以尝试从这个类继承并观察结果? - Arve Systad

3

我使用IWeb驱动程序列表逐行在所有浏览器上执行测试:

[ClassInitialize]
        public static void ClassInitialize(TestContext context) {
            drivers = new List<IWebDriver>();
            firefoxDriver = new FirefoxDriver();
            chromeDriver = new ChromeDriver(path);
            ieDriver = new InternetExplorerDriver(path);
            drivers.Add(firefoxDriver);
            drivers.Add(chromeDriver);
            drivers.Add(ieDriver);
            baseURL = "http://localhost:4444/";
        }

    [ClassCleanup]
    public static void ClassCleanup() {
        drivers.ForEach(x => x.Quit());
    }

..and then am able to write tests like this:

[TestMethod]
        public void LinkClick() {
            WaitForElementByLinkText("Link");
            drivers.ForEach(x => x.FindElement(By.LinkText("Link")).Click());
            AssertIsAllTrue(x => x.PageSource.Contains("test link")); 
        }

在这里,我正在编写自己的方法WaitForElementByLinkText和AssertIsAllTrue,以便为每个驱动程序执行操作,并且如果出现任何失败情况,输出一条帮助我确定可能失败的浏览器的消息:

 public void WaitForElementByLinkText(string linkText) {
            List<string> failedBrowsers = new List<string>();
            foreach (IWebDriver driver in drivers) {
                try {
                    WebDriverWait wait = new WebDriverWait(clock, driver, TimeSpan.FromSeconds(5), TimeSpan.FromMilliseconds(250));
                    wait.Until((d) => { return d.FindElement(By.LinkText(linkText)).Displayed; });
                } catch (TimeoutException) {
                    failedBrowsers.Add(driver.GetType().Name + " Link text: " + linkText);
                }
            }
            Assert.IsTrue(failedBrowsers.Count == 0, "Failed browsers: " + string.Join(", ", failedBrowsers));
        }

IEDriver非常缓慢,但是这将使3个主要的浏览器可以“并排运行测试”。


1

我曾经也遇到同样的问题,最终我找到了解决方法

因此,当你安装插件后,就可以控制在哪个浏览器中测试场景。

功能示例:

@Browser:IE
@Browser:Chrome
Scenario Outline: Add Two Numbers
    Given I navigated to /
    And I have entered <SummandOne> into summandOne calculator
    And I have entered <SummandTwo> into summandTwo calculator
    When I press add
    Then the result should be <Result> on the screen
    Scenarios:
        | SummandOne | SummandTwo | Result |
        | 50 | 70 | 120 |
        | 1 | 10 | 11 |

Implementation

[Given(@"I have entered '(.*)' into the commentbox")]
public void GivenIHaveEnteredIntoTheCommentbox(string p0)
{
            Browser.Current.FindElement(By.Id("comments")).SendKeys(p0);
}

更多信息


1

好的,一个解决方案是使用包装器测试来设置ISelenium对象与不同的浏览器。然后将该对象传递给所有其他测试,它们使用它而不是像以前一样自己设置新的对象。

缺点是,我最终会得到每个浏览器一个大测试。这也不是最好的解决方案。还在寻找...

编辑:

花了更多时间研究这个问题。我想出的解决方案是在解决方案中有一个文本文件,指定用于测试的浏览器。 NUnit在实例化Selenium对象时会获取设置。

我正在使用CruiseControl.NET运行自动构建和测试。而且我不仅运行一次测试,而是配置它们运行两次。但在每个测试之前,我都会运行一个命令行命令,以更改配置文本文件中的浏览器。

<exec>
    <executable>cmd</executable>
    <buildArgs>/C echo firefox C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe > F:\...\selenium_browser.txt</buildArgs>
</exec>
<exec>
    <executable>F:\...\NUnit 2.5.7\bin\net-2.0\nunit-console.exe</executable>
    <baseDirectory>F:\...\bin\Debug</baseDirectory>
    <buildArgs>F:\...\...nunit /xml:"F:\CCXmlLog\Project\nunit-results.xml" /noshadow</buildArgs>
    <successExitCodes>0</successExitCodes>
    <buildTimeoutSeconds>1200</buildTimeoutSeconds>
</exec>

<exec>
    <executable>cmd</executable>
    <buildArgs>/C echo googlechrome C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe > F:\...\selenium_browser.txt</buildArgs>
</exec>
<exec>
    <executable>F:\...\NUnit 2.5.7\bin\net-2.0\nunit-console.exe</executable>
    <baseDirectory>F:\...\bin\Debug</baseDirectory>
    <buildArgs>F:\...\...nunit /xml:"F:\CCXmlLog\Project\nunit-results.xml" /noshadow</buildArgs>
    <successExitCodes>0</successExitCodes>
    <buildTimeoutSeconds>1200</buildTimeoutSeconds>
</exec>

原来您也可以使用<exec>元素指定环境变量,这将是避免文本文件的一种方法。 - Edgar

1

0

一定有更好的方法,但你可以使用T4模板为每个浏览器生成重复的测试类 - 实际上是自动复制和粘贴每个浏览器的测试。


是的,一定有更好的方法。 - Edgar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接