在步骤定义文件之间共享相同的 selenium WebDriver
Sharing same selenium WebDriver between step definition files
现在我们正在努力采用 Cucumber 对我们的 Java8/Spring 应用程序进行 运行 功能测试。我们希望我们的步骤定义文件尽可能保持干燥,因此计划在不同的功能文件中使用相同的步骤定义。由于我们使用 selenium WebDriver
来驱动我们的测试,因此我们需要在步骤定义之间共享相同的驱动程序。
为了演示为什么拥有多个驱动程序对我们来说是个问题,假设一个功能文件定义了两个步骤:一个导航到页面,另一个断言该页面上出现一行。如果这两个步骤恰好在单独的文件中定义,则第一步定义将使用其驱动程序导航到页面。当第二步定义 运行 是针对其驱动程序的断言时,它还没有导航到页面(因为这些操作转到了另一个驱动程序)并且测试失败了。
我们尝试实现每个步骤定义文件将扩展的基础 class(包含驱动程序)。事实证明,Cucumber 实例化了每个步骤定义的一个实例 class,因此我们最终得到每个步骤定义都有不同的 WebDriver
个实例。
我们考虑过使用 Spring 在每个步骤定义文件中注入 WebDriver
的实例,但我相信这会导致与上述相同的问题。
我知道可以使用单例模式来实现这一点,但我们的问题似乎很常见,而且单例模式感觉有点矫枉过正。这实际上是处理它的正确方法吗?还是我遗漏了一些非常明显的东西?
预先感谢您的帮助!
我建议您使用 pico-container 作为依赖注入框架与 cucumber-jvm
一起使用。
使用 PicoContainer,您可以将 'base' class 与 WebDriver 的实例一起使用,然后将此基础 class 自动传递给任何其他 class。如果您愿意,甚至可以直接传递网络驱动程序。
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
示例:
Base class 与 WebDriver 的实例:
public class ContextSteps {
private static boolean initialized = false;
private WebDriver driver;
@Before
public void setUp() throws Exception {
if (!initialized) {
// initialize the driver
driver = = new FirefoxDriver();
initialized = true;
}
}
public WebDriver getDriver() {
return driver;
}
}
其他 class 通过 pico-container DI 访问 webDriver 的人。
public class OtherClassSteps {
private ContextSteps contextSteps;
// PicoContainer injects class ContextSteps
public OtherClassSteps (ContextSteps contextSteps) {
this.contextSteps = contextSteps;
}
@Given("^Foo step$")
public void fooStep() throws Throwable {
// Access WebDriver instance
WebDriver driver = contextSteps.getDriver();
}
}
希望对您有所帮助。
这个问题是老问题了,我在问了这个问题后不久就离开了这个项目,但我回去查看了我们放置的代码(使用单例模式),这就是我们最终得到的。我完全忘记了为什么我们不能使用 pico-container
(这可能是组织限制),但如果您可以使用额外的库,我记得该解决方案效果很好。
我会将其保留为已接受的答案,但希望此解决方案对那些发现自己处于与我几年前相似的位置的人有用。
public class TestingBase {
private static TestingBase instance;
private static WebDriver driver;
private static Thread CLOSE_DRIVER = new Thread() {
@Override
public void run() {
driver.close();
}
};
static {
Runtime.getRuntime().addShutdownHook(CLOSE_DRIVER);
}
private TestingBase() {
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setJavascriptEnabled(true);
desiredCapabilities.setCapability("takesScreenshot", false);
desiredCapabilities.setCapability("handlesAlerts", true);
desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new String[]{
"--web-security=false",
"--ssl-protocol=TLSv1",
"--ignore-ssl-errors=true",
"--webdriver-loglevel=ERROR",
"--webdriver-logfile=/var/log/phantomjs/ghostrdriver.log"
});
desiredCapabilities.setCapability("elementScrollBehavior",true);
driver = new PhantomJSDriver(desiredCapabilities);
}
public static TestingBase getTestingBase() {
if (instance == null) {
instance = new TestingBase();
}
return instance;
}
public static WebDriver getDriver() {
return getTestingBase().driver;
}
}
现在我们正在努力采用 Cucumber 对我们的 Java8/Spring 应用程序进行 运行 功能测试。我们希望我们的步骤定义文件尽可能保持干燥,因此计划在不同的功能文件中使用相同的步骤定义。由于我们使用 selenium WebDriver
来驱动我们的测试,因此我们需要在步骤定义之间共享相同的驱动程序。
为了演示为什么拥有多个驱动程序对我们来说是个问题,假设一个功能文件定义了两个步骤:一个导航到页面,另一个断言该页面上出现一行。如果这两个步骤恰好在单独的文件中定义,则第一步定义将使用其驱动程序导航到页面。当第二步定义 运行 是针对其驱动程序的断言时,它还没有导航到页面(因为这些操作转到了另一个驱动程序)并且测试失败了。
我们尝试实现每个步骤定义文件将扩展的基础 class(包含驱动程序)。事实证明,Cucumber 实例化了每个步骤定义的一个实例 class,因此我们最终得到每个步骤定义都有不同的 WebDriver
个实例。
我们考虑过使用 Spring 在每个步骤定义文件中注入 WebDriver
的实例,但我相信这会导致与上述相同的问题。
我知道可以使用单例模式来实现这一点,但我们的问题似乎很常见,而且单例模式感觉有点矫枉过正。这实际上是处理它的正确方法吗?还是我遗漏了一些非常明显的东西?
预先感谢您的帮助!
我建议您使用 pico-container 作为依赖注入框架与 cucumber-jvm
一起使用。
使用 PicoContainer,您可以将 'base' class 与 WebDriver 的实例一起使用,然后将此基础 class 自动传递给任何其他 class。如果您愿意,甚至可以直接传递网络驱动程序。
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
示例:
Base class 与 WebDriver 的实例:
public class ContextSteps {
private static boolean initialized = false;
private WebDriver driver;
@Before
public void setUp() throws Exception {
if (!initialized) {
// initialize the driver
driver = = new FirefoxDriver();
initialized = true;
}
}
public WebDriver getDriver() {
return driver;
}
}
其他 class 通过 pico-container DI 访问 webDriver 的人。
public class OtherClassSteps {
private ContextSteps contextSteps;
// PicoContainer injects class ContextSteps
public OtherClassSteps (ContextSteps contextSteps) {
this.contextSteps = contextSteps;
}
@Given("^Foo step$")
public void fooStep() throws Throwable {
// Access WebDriver instance
WebDriver driver = contextSteps.getDriver();
}
}
希望对您有所帮助。
这个问题是老问题了,我在问了这个问题后不久就离开了这个项目,但我回去查看了我们放置的代码(使用单例模式),这就是我们最终得到的。我完全忘记了为什么我们不能使用 pico-container
(这可能是组织限制),但如果您可以使用额外的库,我记得该解决方案效果很好。
我会将其保留为已接受的答案,但希望此解决方案对那些发现自己处于与我几年前相似的位置的人有用。
public class TestingBase {
private static TestingBase instance;
private static WebDriver driver;
private static Thread CLOSE_DRIVER = new Thread() {
@Override
public void run() {
driver.close();
}
};
static {
Runtime.getRuntime().addShutdownHook(CLOSE_DRIVER);
}
private TestingBase() {
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setJavascriptEnabled(true);
desiredCapabilities.setCapability("takesScreenshot", false);
desiredCapabilities.setCapability("handlesAlerts", true);
desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new String[]{
"--web-security=false",
"--ssl-protocol=TLSv1",
"--ignore-ssl-errors=true",
"--webdriver-loglevel=ERROR",
"--webdriver-logfile=/var/log/phantomjs/ghostrdriver.log"
});
desiredCapabilities.setCapability("elementScrollBehavior",true);
driver = new PhantomJSDriver(desiredCapabilities);
}
public static TestingBase getTestingBase() {
if (instance == null) {
instance = new TestingBase();
}
return instance;
}
public static WebDriver getDriver() {
return getTestingBase().driver;
}
}