多个浏览器上的Selenium Grid:每个测试用例应该为每个浏览器分别设置类吗?

时间:2013-01-17 17:48:37

标签: selenium webdriver selenium-webdriver selenium-grid

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

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

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

对于那些受雇使用Selenium的QA工程师的人来说,这里最好的做法是什么?

4 个答案:

答案 0 :(得分:4)

我目前正在开展一个每天运行约75k-90k测试的项目。我们将浏览器作为参数传递给测试。理由是:

  1. 正如您在问题中提到的,这有助于维护。
  2. 我们没有看到太多特定于浏览器的代码。如果你有太多的浏览器特定代码,那么我会说webdriver本身有问题。因为,selenium / webdriver的一个优点是只需编写一次代码并在任何支持的浏览器上运行它。
  3. 我在代码结构和你提到的问题之间看到的区别是,我没有针对每个测试用例的测试类。测试根据我测试的功能进行划分,每个功能都有一个类。该类将所有测试作为方法。我使用testNG,以便可以并行调用这些方法。可能这不适合您的AUT。

答案 1 :(得分:3)

如果你保留你在问题中提到的代码结构,迟早维护它将成为一场噩梦。尝试坚持规则:所有浏览器(环境)的相同测试代码(一次编写)。

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

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

您可以随时轻松修改和添加环境。您可以注意到,我正在使用它来创建和检索DesiredCapabilities,稍后将用于创建特定的WebDriver。

为了让测试针对所有已定义的环境运行,我使用了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,包含在某些浏览器中恰好不同(或有bug)的公共代码。在同一个包中,我有特定于测试中使用的每个浏览器的类,每个类都扩展了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测试配置此功能(自4.7版开始提供)。

答案 2 :(得分:1)

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

我不知何故相信为每个浏览器创建一个类并不是一个好主意。想象一下流量的变化,或者有一些在这里和那里,你有3个类而不是一个。如果浏览器数量增加,那么又增加一个类。

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

答案 3 :(得分:0)

我是这样做的,但请记住,这是纯粹的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

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

  1. 如果当前参数测试是此浏览器类型的第一次测试,并且尚未打开窗口,请打开当前类型的新浏览器窗口。
  2. 如果浏览器已打开且浏览器类型相同,则只需重新使用相同的驱动程序对象。
  3. 如果浏览器打开了与当前测试不同的类型,则关闭浏览器并重新打开正确类型的浏览器。
  4. 当然,我的回答并没有告诉你如何处理这些参数:我留给你了解。