如果您保留问题中提到的代码结构,那么维护它迟早会成为一场噩梦。尝试遵守规则:所有浏览器(环境)使用相同的测试代码(一次编写)。
这种情况将迫使您解决两个问题:
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 开始可用)。