【问题标题】:Selenium Grid on Multiple Browsers: should each test case have separate class for each browser?多个浏览器上的 Selenium Grid:每个测试用例是否应该为每个浏览器设置单独的类?
【发布时间】:2013-01-17 17:48:36
【问题描述】:

我正在尝试整合我的第一个数据驱动测试框架,该框架通过 Selenium Grid/WebDriver 在多个浏览器上运行测试。现在,我在它自己的类中拥有每个测试用例,并且我对浏览器进行了参数化,因此它对每个浏览器运行每个测试用例一次。

这在大型测试框架中很常见吗?或者,是否应该将每个测试用例复制并微调到它自己的类中的每个浏览器?那么,如果我正在测试 chrome、firefox 和 IE,是否应该有每个类,例如:“TestCase1Chrome”、“TestCase1FireFox”、“TestCase1IE”?或者只是“TestCase1”并将测试参数化为每个浏览器运行 3 次?只是想知道别人是怎么做到的。

将测试参数化为每个测试用例的单个类可以更轻松地维护非浏览器特定的代码,同时为每个浏览器用例复制一个类可以更轻松地维护特定于浏览器的代码。当我说浏览器特定的代码时,例如,单击一个项目。在 ChromeDriver 上,您无法单击某些元素的中间,而在 FirefoxDriver 上,您可以。因此,您可能需要两个不同的代码块来单击一个元素(当它在中间不可单击时)。

对于那些使用 Selenium 的 QA 工程师,这里的最佳做法是什么?

【问题讨论】:

    标签: selenium webdriver selenium-webdriver selenium-grid


    【解决方案1】:

    我目前正在从事一个每天运行大约 75k - 90k 次测试的项目。我们将浏览器作为参数传递给测试。原因是:

    1. 正如您在问题中提到的,这有助于维护。
    2. 我们没有看到太多特定于浏览器的代码。如果您的浏览器特定代码过多,那么我会说 webdriver 本身存在问题。因为,selenium/webdriver 的优势之一是编写一次代码,然后在任何支持的浏览器上运行它。

    我看到的代码结构与您提到的代码结构之间的区别是,我没有针对每个测试用例的测试类。测试根据我测试的功能进行划分,每个功能都有一个类。该类将所有测试作为方法保存。我使用 testNG 以便可以并行调用这些方法。可能这不适合您的 AUT。

    【讨论】:

    • 这也是我建议/暗示的方式(见下面我的回答)。我目前正在尝试使用 JDK1.7 和 java.util.concurrent.RecursiveTask,所以我不需要使用 TestNG 来并行运行并且可以坚持使用 JUnit。这是因为我使用 Gradle 进行单元测试报告,不需要 TestNG 报告。
    • 您还可以使用 Maven Surefire 或 Gradle 调用并行。您不必“必须”使用 TestNG。
    【解决方案2】:

    如果您保留问题中提到的代码结构,那么维护它迟早会成为一场噩梦。尽量遵守规则:所有浏览器(环境)使用相同的测试代码(一次编写)。

    这种情况会迫使你解决两个问题:

    1) 如何为所有选定的浏览器运行测试

    2) 如何在不污染测试代码的情况下应用特定的浏览器变通方法

    其实,这似乎是你的问题。

    这是我解决第一个问题的方法。 首先,我定义了我要测试的所有环境。我将要运行测试的所有条件称为“环境”:浏览器名称、版本号、操作系统等。因此,与测试代码分开,我创建了一个这样的枚举:

    public enum Environments {
    
        FF_18_WIN7("firefox", "18", Platform.WINDOWS),
        CHR_24_WIN7("chrome", "24", Platform.WINDOWS),
        IE_9_WIN7("internet explorer", "9", Platform.WINDOWS)
        ;
    
        private final DesiredCapabilities capabilities;
        private final String browserName;
        private final String version;
        private final Platform platform;
    
        Environments(final String browserName, final String version, final Platform platform) {
            this.browserName = browserName;
            this.version = version;
            this.platform = platform;
            capabilities = new DesiredCapabilities();
        }
    
        public DesiredCapabilities capabilities() {
            capabilities.setBrowserName(browserName);
            capabilities.setVersion(version);
            capabilities.setPlatform(platform);
            return this.capabilities;
        }
    
        public String browserName() {
            return browserName;
        }
    }
    

    您可以随时根据需要轻松修改和添加环境。如您所见,我使用它来创建和检索稍后将用于创建特定 WebDriver 的 DesiredCapabilities。

    为了让测试在所有定义的环境下运行,我使用了 JUnit(在我的例子中是 4.10)org.junit.experimental.theories

    @RunWith(MyRunnerForSeleniumTests.class)
    public class MyWebComponentTestClassIT {
    
        @Rule 
        public MySeleniumRule selenium = new MySeleniumRule();
    
        @DataPoints 
        public static Environments[] enviroments = Environments.values();
    
        @Theory
        public void sample_test(final Environments environment) {
    
            Page initialPage = LoginPage.login(selenium.driverFor(environment), selenium.getUserName(), selenium.getUserPassword());
    
            // your test code here
    
        }   
    }
    

    测试被注释为@Theory(不像@Test,就像在普通的JUnit测试中一样)并被传递一个参数。然后将针对此参数的所有定义值运行每个测试,这应该是注释为@DataPoints 的值数组。此外,您应该使用从org.junit.experimental.theories.Theories 扩展的跑步者。我使用org.junit.rules 来准备我的测试,在那里放置所有必要的管道。如您所见,我也通过规则获得了特定的功能驱动程序。尽管您可以在测试中直接使用以下代码:

    RemoteWebDriver driver = new RemoteWebDriver(new URL(some_url_string), environment.capabilities());
    

    关键在于,将它包含在规则中,您只需编写一次代码并将其用于所有测试。 至于 Page 类,它是我放置所有使用驱动程序功能(查找元素、导航等)的代码的类。这样,测试代码再次保持整洁和清晰,并且您只需编写一次并在所有测试中使用它。 所以,这是第一个问题的解决方案。 (我知道你可以用 TestNG 做类似的事情,但我没有尝试过。)

    为了解决第二个问题,我创建了一个特殊的包,其中保存了浏览器特定解决方法的所有代码。它由一个抽象类组成,例如BrowserSpecific,它包含在某些浏览器中碰巧不同(或有错误)的公共代码。在同一个包中,我有特定于测试中使用的每个浏览器的类,并且它们中的每一个都扩展了 BrowserSpecific。

    这是您提到的 Chrome 驱动程序错误的工作原理。我在 BrowserSpecific 中创建了一个方法 clickOnButton,其中包含受影响行为的通用代码:

    public abstract class BrowserSpecific {
    
        protected final RemoteWebDriver driver;
    
        protected BrowserSpecific(final RemoteWebDriver driver) {
            this.driver = driver;
        }
    
        public static BrowserSpecific aBrowserSpecificFor(final RemoteWebDriver driver) {
            BrowserSpecific browserSpecific = null;
    
            if (Environments.FF_18_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
                browserSpecific = new FireFoxSpecific(driver);
            }
    
            if (Environments.CHR_24_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
                browserSpecific = new ChromeSpecific(driver);
            }
    
            if (Environments.IE_9_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
                browserSpecific = new InternetExplorerSpecific(driver);
            }
    
            return browserSpecific;
        }
    
        public void clickOnButton(final WebElement button) {
                button.click();
        }
    }
    

    然后我在特定类中重写此方法,例如ChromeSpecific,我放置解决方法代码的位置:

    public class ChromeSpecific extends BrowserSpecific {
    
            ChromeSpecific(final RemoteWebDriver driver) {
                super(driver);
            }
    
            @Override
            public void clickOnButton(final WebElement button) {
    
            // This is the Chrome workaround
    
                String script = MessageFormat.format("window.scrollTo(0, {0});", button.getLocation().y);
                driver.executeScript(script);
    
            // Followed by common behaviour of all the browsers
                super.clickOnButton(button);
            }
    }
    

    当我必须考虑某些浏览器的特定行为时,我会执行以下操作:

     aBrowserSpecificFor(driver).clickOnButton(logoutButton);
    

    代替:

     button.click();
    

    这样,在我的通用代码中,我可以轻松识别解决方法的应用位置,并将解决方法与通用代码隔离开来。我发现它易于维护,因为通常会解决错误,并且可能或应该更改或消除变通方法。

    关于执行测试的最后一句话。当您将使用 Selenium Grid 时,您将希望使用并行运行测试的可能性,因此请记住为您的 JUnit 测试配置此功能(从 v. 4.7 开始可用)。

    【讨论】:

    • 我喜欢你的解决方案。
    【解决方案3】:

    我们在我们的组织中使用 testng,我们使用 testng 提供的参数选项来指定环境,即要使用的浏览器、要运行的机器以及 env 配置所需的任何其他配置。浏览器名称通过 xml 文件发送,该文件控制需要运行的内容和位置。它被设置为全局变量。我们额外做的是,我们有可以覆盖这些全局变量的自定义注释,即如果一个测试非常明确地只在 chrome 上运行而没有其他浏览器,那么我们在自定义注释上指定相同的内容。所以,即使参数说run on FF,如果用chrome注解,它总是会在chrome上运行。

    不知何故,我认为为每个浏览器创建一个类并不是一个好主意。想象一下流程发生了变化,或者这里和那里有一些变化,你有 3 个类要改变而不是一个。如果浏览器的数量增加,那么就会增加一个类。

    我的建议是提取特定于浏览器的代码。因此,如果点击行为是特定于浏览器的,则覆盖它以根据浏览器进行适当的检查或故障处理。

    【讨论】:

      【解决方案4】:

      我是这样做的,但请记住,这是纯粹的 WebDriver,没有考虑到 Grid 或 RC:

      // Utility class snippet
      // Test classes import this with:  import static utility.*;
      public static WebDriver driver;
      public static void initializeBrowser( String type ) {
        if ( type.equalsIgnoreCase( "firefox" ) ) {
          driver = new FirefoxDriver();
        } else if ( type.equalsIgnoreCase( "ie" ) ) {
          driver = new InternetExplorerDriver();
        }
        driver.manage().timeouts().implicitlyWait( 10000, TimeUnit.MILLISECONDS );
        driver.manage().window().setPosition(new Point(200, 10));
        driver.manage().window().setSize(new Dimension(1200, 800));
      }
      

      现在,使用 JUnit 4.11+ 您的参数文件需要如下所示:

      firefox, test1, param1, param2
      firefox, test2, param1, param2
      firefox, test3, param1, param2
      ie, test1, param1, param2
      ie, test2, param1, param2
      ie, test3, param1, param2
      

      然后,使用单个 .CSV 参数化测试类(您打算使用它启动多种浏览器类型),在 @Before 注释方法中执行以下操作:

      1. 如果当前参数测试是此浏览器类型的第一个测试,并且不存在已打开的窗口,则打开当前类型的新浏览器窗口。
      2. 如果浏览器已经打开并且浏览器类型相同,则只需重新使用相同的驱动程序对象。
      3. 如果浏览器打开的类型与当前测试不同,则关闭浏览器并重新打开正确类型的浏览器。

      当然,我的回答并没有告诉你如何处理参数:我留给你自己弄清楚。

      【讨论】:

      • 是的,这正是我所做的。但我要问的是,当您调用该函数时,您是否对给定测试用例的单个类中的类型参数(浏览器)进行了参数化?或者您是否重复测试用例,每种浏览器类型都有自己的类?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-22
      相关资源
      最近更新 更多