英文:
Is it possible to execute Cucumber Tests multiple times based on tags?
问题
提供我有以下的特性文件,其中有两个具有不同标签的场景,如下所示:
```java
Feature: 主页功能
主页的测试
@Desktop @Tablet @Mobile
Scenario: 应该显示正确的页面标题
假设我导航到主页
那么页面标题应该为“test”
@Desktop @Tablet
Scenario: 应该显示正确的正文文本
假设我导航到主页
那么正文文本应该为“test”
并且我还有以下的 Hooks
、ChromeWebController
、HomeSteps
和 RunCucumberTest
类,是否可能在不同设备上多次运行这些场景。例如,带有 @Desktop
、@Tablet
和 @Mobile
标签的场景是否可以在三个不同的设备上分别执行三次,而带有 @Desktop
和 @Tablet
标签的场景是否可以在两个不同的设备上分别执行两次?或者这是否需要不同的方法?
package hooks;
import controllers.ChromeWebController;
import fixtures.HomePageFixture;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
private final ChromeWebController chromeWebController;
private Scenario scenario;
public Hooks(ChromeWebController chromeWebController) {
this.chromeWebController = chromeWebController;
}
@Before
public void beforeScenarioSteps(Scenario scenario) {
this.scenario = scenario;
chromeWebController.setDeviceType(scenario.getSourceTagNames());
chromeWebController.setupChromeDriver();
scenario.log(HomePageFixture.HOME_PAGE_URL);
}
@After
public void afterScenarioSteps() {
chromeWebController.quitWebDriver();
}
}
package controllers;
import io.github.bonigarcia.wdm.WebDriverManager;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class ChromeWebController {
private WebDriver webDriver;
private String deviceType;
public ChromeWebController() {
}
public void setupChromeDriver() {
ChromeOptions options = new ChromeOptions();
Map<String, String> mobileEmulation = new HashMap<>();
WebDriverManager.chromedriver().setup();
options.addArgument("--window-size=1366,768");
if (getDeviceType().equals("Tablet")) {
mobileEmulation.put("deviceName", "iPad");
options.setExperimentalOption("mobileEmulation", mobileEmulation);
} else if (getDeviceType().equals("Mobile")) {
mobileEmulation.put("deviceName", "iPhone X");
options.setExperimentalOption("mobileEmulation", mobileEmulation);
}
this.webDriver = new ChromeDriver(options);
}
public WebDriver getWebDriver() {
if (webDriver == null) {
throw new WebDriverException("WebDriver has not been created, please ensure setupChromeDriver has been called.");
}
return webDriver;
}
public void quitWebDriver() {
webDriver.quit();
}
public void setDeviceType(Collection<String> scenarioTags) {
if (deviceType != null) {
throw new IllegalArgumentException("deviceType can only be set once.");
}
var scenarioTagsToString = scenarioTags.toString();
if (scenarioTagsToString.contains("@Desktop")) {
deviceType = "Desktop";
} else if (scenarioTagsToString.contains("@Tablet")) {
deviceType = "Tablet";
} else if (scenarioTags.toString().contains("@Mobile")) {
deviceType = "Mobile";
} else {
throw new IllegalArgumentException("Desktop/Tablet/Mobile scenario tag has not been implemented in the feature file.");
}
}
public String getDeviceType() {
if (deviceType.equals("")) {
throw new IllegalArgumentException("deviceType has not been set.");
}
return deviceType;
}
}
package steps.home;
import controllers.ChromeWebController;
import fixtures.HomePageFixture;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class HomeSteps{
private final WebDriver webDriver;
public HomeSteps(ChromeWebController chromeWebController) {
this.webDriver = chromeWebController.getWebDriver();
}
@Given("I navigate to the Home page")
public void iNavigateToTheHomePage() {
webDriver.get(HomePageFixture.HOME_PAGE_URL);
}
@Then("The body text should read {string}")
public void theBodyTextShouldRead(String expectedText) {
String actualText = webDriver.findElement(By.id("test")).getText();
assert (actualText).equals(expectedText);
}
}
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;
@CucumberOptions(publish = true)
public class RunCucumberTest extends AbstractTestNGCucumberTests {
@Override
@DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
<details>
<summary>英文:</summary>
Providing I have the following Feature File with two scenarios that have different tags such as the below:
Feature: Home Feature
Tests for the Home page
@Desktop @Tablet @Mobile
Scenario: I should see the correct page title
Given I navigate to the Home page
Then the page title should read "test"
@Desktop @Tablet
Scenario: I should see the correct body text
Given I navigate to the Home page
Then the body text should read "test"
And I also have the following ```Hooks```, ```ChromeWebController```, ```HomeSteps``` & ```RunCucumberTest``` classes, would it be possible to run the scenarios multiple times on different devices. For example, could the scenario with ```@Desktop```, ```@Tablet``` & ```@Mobile``` be executed three times, once on each device, whilst, the scenario with ```@Desktop``` ```@Tablet``` be executed two times, again, once on each device? Or would this require a different approach?
package hooks;
import controllers.ChromeWebController;
import fixtures.HomePageFixture;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
private final ChromeWebController chromeWebController;
private Scenario scenario;
public Hooks(ChromeWebController chromeWebController) {
this.chromeWebController = chromeWebController;
}
@Before
public void beforeScenarioSteps(Scenario scenario) {
this.scenario = scenario;
chromeWebController.setDeviceType(scenario.getSourceTagNames());
chromeWebController.setupChromeDriver();
scenario.log(HomePageFixture.HOME_PAGE_URL);
}
@After
public void afterScenarioSteps() {
chromeWebController.quitWebDriver();
}
}
package controllers;
import io.github.bonigarcia.wdm.WebDriverManager;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
public class ChromeWebController {
private WebDriver webDriver;
private String deviceType;
public ChromeWebController() {
}
public void setupChromeDriver() {
ChromeOptions options = new ChromeOptions();
Map<String, String> mobileEmulation = new HashMap<>();
WebDriverManager.chromedriver().setup();
options.addArgument("--window-size=1366,768");
if (getDeviceType().equals("Tablet")) {
mobileEmulation.put("deviceName", "iPad");
options.setExperimentalOption("mobileEmulation", mobileEmulation);
} else if (getDeviceType().equals("Mobile")) {
mobileEmulation.put("deviceName", "iPhone X");
options.setExperimentalOption("mobileEmulation", mobileEmulation);
}
this.webDriver = new ChromeDriver(options);
}
public WebDriver getWebDriver() {
if (webDriver == null) {
throw new WebDriverException("WebDriver has not been created, please ensure setupChromeDriver has been called.");
}
return webDriver;
}
public void quitWebDriver() {
webDriver.quit();
}
public void setDeviceType(Collection<String> scenarioTags) {
if (deviceType != null) {
throw new IllegalArgumentException("deviceType can only be set once.");
}
var scenarioTagsToString = scenarioTags.toString();
if (scenarioTagsToString.contains("@Desktop")) {
deviceType = "Desktop";
} else if (scenarioTagsToString.contains("@Tablet")) {
deviceType = "Tablet";
} else if (scenarioTags.toString().contains("@Mobile")) {
deviceType = "Mobile";
} else {
throw new IllegalArgumentException("Desktop/Tablet/Mobile scenario tag has not been implemented in the feature file.");
}
}
public String getDeviceType() {
if (deviceType.equals("")) {
throw new IllegalArgumentException("deviceType has not been set.");
}
return deviceType;
}
}
package steps.home;
import controllers.ChromeWebController;
import fixtures.HomePageFixture;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class HomeSteps{
private final WebDriver webDriver;
public HomeSteps(ChromeWebController chromeWebController) {
this.webDriver = chromeWebController.getWebDriver();
}
@Given("I navigate to the Home page")
public void iNavigateToTheHomePage() {
webDriver.get(HomePageFixture.HOME_PAGE_URL);
}
@Then("The body text should read {string}")
public void theBodyTextShouldRead(String expectedText) {
String actualText = webDriver.findElement(By.id("test")).getText();
assert (actualText).equals(expectedText);
}
}
```import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;
@CucumberOptions(publish = true)
public class RunCucumberTest extends AbstractTestNGCucumberTests {
@Override
@DataProvider(parallel = true)
public Object[][] scenarios() {
return super.scenarios();
}
}
答案1
得分: 1
创建两个(基本上等于设备的)用于特定设备钩子的包。
移动设备类型的钩子放在 `mobile` 包中:
```java
package mobile;
public class MobileHook {
@Before("@mobile")
public void before(Scenario scenario) {
System.out.println("MOBILE");
}
}
平板设备类型的钩子放在 tablet
包中:
package tablet;
public class TabletHook {
@Before("@tablet")
public void before() {
System.out.println("TABLET");
}
}
通用的 Glue 代码应该放在一个单独的包中,比如 stepdefs
。
相应地修改运行器:
@CucumberOptions(plugin = { "summary" }, tags = "@mobile", glue = {"mobile", "stepdefs"})
public class RunCukeMobileIT
@CucumberOptions(plugin = { "summary" }, tags = "@tablet", glue = {"tablet", "stepdefs"})
public class RunCukeTabletIT
基本上是将可见性降低到相关的钩子。我猜您需要修改初始化驱动程序的代码。
<details>
<summary>英文:</summary>
Create two (basically equal to the devices) packages for device specific hook.
Hook for mobile type in package `mobile`
package mobile;
public class MobileHook {
@Before("@mobile")
public void before(Scenario scenario) {
System.out.println("MOBILE");
}
}
Hook for tablet type in package `tablet`
package tablet;
public class TabletHook {
@Before("@tablet")
public void before() {
System.out.println("TABLET");
}
}
The common glue code should be kept in a separate package say `stepdefs`.
Modify the runners accordingly.
@CucumberOptions(plugin = { "summary" }, tags = "@mobile", glue = {"mobile", "stepdefs"})
public class RunCukeMobileIT
@CucumberOptions(plugin = { "summary" }, tags = "@tablet", glue = {"tablet", "stepdefs"})
public class RunCukeTabletIT
Basically reducing the visibility to the relevant hook. Guess u will need to modify the code which initializes the driver.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论