8

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

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

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

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

4

4 回答 4

4

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

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

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

于 2013-01-24T23:16:22.597 回答
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;
    }
}

随时可以轻松修改和添加环境。如您所见,我使用它来创建和检索稍后将用于创建特定 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 开始可用)。

于 2013-02-01T14:33:53.650 回答
1

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

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

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

于 2013-01-25T10:41:28.310 回答
0

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

// 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. 如果浏览器打开的类型与当前测试不同,则关闭浏览器并重新打开正确类型的浏览器。

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

于 2013-01-17T21:20:26.943 回答