如何同时在多个浏览器上运行测试?硒网格,C#,Specflow,NUnit

时间:2019-06-28 13:00:21

标签: c# selenium selenium-webdriver grid specflow

几天来,我一直在指南和YouTube视频之间跳来跳去,试图在现有项目上实现Selenium Grid 2,但我陷入困境,请帮忙!

我们的框架是Specflow 3.0.220,Selenium WebDriver 3.141.0,C#,NUnit 3.12.0,Selenium Grid selenium-server-standalone-3.141.59。

实现Selenium Grid 2的最初目标如下:

  1. 在本地计算机上设置集线器和节点=完成。
  2. 通过其中一个节点进行测试=完成。
  3. 同时在所有节点上运行测试=头痛。

关于第2项,我设置了两个节点,一个是Chrome节点,一个是Firefox节点。我可以对它们两个进行测试,但不能同时进行。

我觉得我在这里错过了一个难题。

这里是设置:

Scenario Outline: Log in
    Given I launch the site for <profile> and <environment> and <parallelEnvironment>
    When I log in to the Normal account
    Then I see that I am logged in

        Examples:
        | profile  | environment | parallelEnvironment |
        | parallel | Chrome75    | grid                |

如果配置文件是并行的,而parallelEnvironment是网格,则将忽略环境。 parallelEnvironment的原因是,在设置Selenium Grid的过程中,我们可能仍会临时使用Browserstack。

这些步骤使用相关的步骤文件等和页面文件(但不使用已弃用的Page Object Model)。

驱动程序设置如下:

namespace OurAutomation
{
    [Binding]
    public sealed class BrowserStack
    {
        private BrowserStackDriver bsDriver;
        public static BrowserStackDriver bdriver;

        [BeforeScenario]
        public void BeforeScenario()
        {
            bsDriver = new BrowserStackDriver();
            bdriver = bsDriver;
        }

        [AfterScenario]
        public void AfterScenario()
        {
            bsDriver.Cleanup();
        }
    }

    public class CustomRemoteWebDriver : RemoteWebDriver
    {
        public CustomRemoteWebDriver(Uri remoteAddress, ChromeOptions options) : base(remoteAddress, options)
        {
        }

        public string getSessionID()
        {
            return base.SessionId.ToString();
        }
    }

    public class BrowserStackDriver
    {
        private IWebDriver driver;
        public static bool isBrowserStack = false;
        public static string Platform;
        public static string theEnvironment;
        public static string sessionId;

        public BrowserStackDriver()
        {

        }

        public string GetString(string property)
        {
            if (TestContext.Parameters[property] == null)
            {
                throw new ArgumentException("Property does not exist, does not have a value, or a test setting is not selected. You may need to add the .runsettings file in Visual Studio (Test > Test Settings > Select Test Settings File).");
            }
            else
            {
                return TestContext.Parameters[property].ToString();
            }
        }

        public IWebDriver Init(string profile, string environment, string parallelEnvironment)
        {
            String testString = GetString("BuildNumber");

            theEnvironment = environment;

            NameValueCollection caps = ConfigurationManager.GetSection("capabilities/" + profile) as NameValueCollection;
            NameValueCollection settings = ConfigurationManager.GetSection("environments/" + environment) as NameValueCollection;

            ChromeOptions chromeOptions = new ChromeOptions();

            if (profile == "single")
            {
// logic to invoke relevant browser locally based on Specflow parameter 'profile'
                Thread.Sleep(3000);
            }
            else if (profile == "parallel")
            {    
                if (parallelEnvironment == "browserstack")
                {
                    foreach (string key in caps.AllKeys)
                    {
                        chromeOptions.AddAdditionalCapability(key, caps[key]);
                    }

                    foreach (string key in settings.AllKeys)
                    {
                        chromeOptions.AddAdditionalCapability(key, settings[key]);
                    }

                    string username = Environment.GetEnvironmentVariable("BROWSERSTACK_USERNAME");

                    if (username == null)
                    {
                        username = ConfigurationManager.AppSettings.Get("user");
                    }

                    string accesskey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY");

                    if (accesskey == null)
                    {
                        accesskey = ConfigurationManager.AppSettings.Get("key");
                    }

                    chromeOptions.AddAdditionalCapability("browserstack.user", username);
                    chromeOptions.AddAdditionalCapability("browserstack.key", accesskey);
                    chromeOptions.AddAdditionalCapability("browserstack.local", "true");
                    chromeOptions.AddAdditionalCapability("build", GetString("BuildNumber"));
                    chromeOptions.AddAdditionalCapability("name", TestContext.CurrentContext.Test.MethodName);
                    chromeOptions.AddAdditionalCapability("project", GetString("Project"));

                    BrowserStackDriver.isBrowserStack = true;

                    driver = new CustomRemoteWebDriver(
                    new Uri("http://" + ConfigurationManager.AppSettings.Get("server") + "/wd/hub/"), chromeOptions);

                    CustomRemoteWebDriver browserRemoteDriver = driver as CustomRemoteWebDriver;
                    sessionId = browserRemoteDriver.getSessionID();
                }
                else if (parallelEnvironment == "grid")
                {
                    driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
                }
            }

            return driver;
        }

        public void Cleanup()
        {
            Thread.Sleep(2000);
            if (isBrowserStack)
            {
                Log.Status status = (TestContext.CurrentContext.Result.Message == null) ? Log.Status.Passed : Log.Status.Failed;
                string reason = (TestContext.CurrentContext.Result.Message == null) ? "Passed" : "Error see exception";

                Log.UpdateTestStatus(status, reason, sessionId);
            }

            driver.Quit();
            driver = null;
        }
    }
}

所以在这里...

                else if (parallelEnvironment == "grid")
                {
                    driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
                }

...我输入节点之一的地址,然后进行测试。但是,我只想将测试发送到中心,然后让它同时在其相关浏览器中的所有活动节点上执行该测试。我该如何实现?指南和视频似乎只是把我带走了。

谢谢

更新:

因此,我朝着正确的方向努力。必须将其还原为基础知识,因此我可以看到如何在现有项目中实现这一点。我已经在网格中完成了这项工作:https://github.com/teixeira-fernando/Parallel-Execution-with-Selenium-Grid

但是我注意到我需要为测试添加属性(以在多个浏览器上同时运行一个测试)...

    namespace Tutorial_parallel_execution
{
    [TestFixture(BrowserType.Chrome)]
    [TestFixture(BrowserType.Firefox)]
    [TestFixture(BrowserType.Opera)]
    [TestFixture(BrowserType.IE)]
    [Parallelizable(ParallelScope.Fixtures)]
    public class GoogleTesting : Hooks
    {
        public GoogleTesting(BrowserType browser) : base(browser)
        {

        }

        [Test]
        public void GoogleTest()
        {
            Driver.Navigate().GoToUrl("http://www.google.com");
            Driver.FindElement(By.Name("q")).SendKeys("selenium");
            Driver.FindElement(By.Name("btnK")).Click();
            Assert.That(Driver.PageSource.Contains("Selenium"), Is.EqualTo(true),
                "The text selenium doenst exist");
        }
    }

}

但是,由于我的项目开始与此SpecFlow Visual Studio extension attempted to use SpecFlow code-behind generator 1.9类似,所以我开始使用SpecFlow.Tools.MsBuild.Generation并失去了对测试(代码隐藏文件)的访问权限以添加属性。我可以添加的唯一属性是[Parallelizable(ParallelScope.Fixtures)],但我必须将其放在AssemblyInfo.cs中-其他属性不能在此处添加。

为了使这项工作有效,我是否需要降级Specflow / Selenium等版本?

2 个答案:

答案 0 :(得分:0)

我能够从https://github.com/minhhoangvn/AutomationFramework剥离使用ThreadLocal实现并行执行所需的代码

答案 1 :(得分:0)

将此添加到您的AssemblyInfo.cs文件中:

[assembly: Parallelizable(ParallelScope.Fixtures)]
[assembly: LevelOfParallelism(4)]

您会在此处看到4是要同时运行的测试数。因此,如果您有2个节点,但要同时运行4个测试,则每个节点将获得2个chrome浏览器。

当您使用MsBuild.Generation时,feature.cs文件仍然存在,它们只是不会出现在Visual Studio中。

创建驱动程序时,您可以尝试将其添加到Hooks.cs文件中:

 ScenarioContext _scenarioContext;
 IWebDriver _currentWebDriver;
 _currentWebDriver = new RemoteWebDriver(new Uri(Utilities.SeleniumHub), options.ToCapabilities(), TimeSpan.FromMinutes(3));
 _scenarioContext.ScenarioContainer.RegisterInstanceAs<IWebDriver>(_currentWebDriver);

然后在完成场景后执行此操作:

    [AfterScenario]
    public void CloseBrowserAfterScenario()
    {
        string driver_process_name = null;
        string browser_process_name = null;
        switch (browser)
        {
            case "Chrome":
                driver_process_name = "chromedriver.exe";
                break;
            case "IEX64":
            case "IEX86":
                driver_process_name = "IEDriverServer.exe";
                break;
            case "Edge":
                driver_process_name = "MicrosoftWebDriver.exe";
                browser_process_name = "MicrosoftEdge.exe";
                break;
            case "Firefox":
                driver_process_name = "geckodriver.exe";
                break;
            default:
                LogMessage(browser + "is not found or not supported... Please update the TestUI.dll.Config File");
                break;
        }

        System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(driver_process_name);

        foreach (System.Diagnostics.Process app_process in process)
        {
            if (!string.IsNullOrEmpty(app_process.ProcessName))
            {
                try
                {
                    app_process.Kill();
                }
                catch
                {
                    FunctionalUtil.LogMessage("app_process.Kill(); failed in CloseBrowserAfterScenario");
                }
            }
        }