大家好:我正在使用 Cucumber、Selenium、TestNG 在 Maven 中创建一个存储库。
我正在尝试做的是从属性文件中获取值以使用 Selenium 发送这些参数,但我无法解决此处生成的问题。
这是我的项目的结构:
.
├── pom.xml
├── selenium_cucumber.iml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── utilities
│ │ │ ├── LoadProperties.java
│ │ │ └── ReportMerger.java
│ │ └── resources
│ │ ├── config.properties
│ │ └── log4j.properties
│ └── test
│ ├── java
│ │ ├── features
│ │ │ └── first.feature
│ │ ├── hooks
│ │ │ ├── ServiceHooks.java
│ │ │ ├── TakesScreenshots.java
│ │ │ └── WebDriverManager.java
│ │ ├── pages
│ │ │ ├── BasePage.java
│ │ │ └── Page_First.java
│ │ ├── runners
│ │ │ └── testNGCucumberRunner.java
│ │ └── steps
│ │ └── Step_First.java
│ └── resources
│ └── testng.xml
└── target
这是我的功能文件:
@test
Feature: Navigation Test
This is example of using Cucumber-JVM with TestNG and Selenium
Scenario: Search google.com to verify google search is working
Given I go to "<Url>" Google
When I query for "<search>" cucumber spring selenium
And click search
Then google page title should become the first page
继续从属性文件 ( config.properties )加载属性的页面
package utilities;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
public class LoadProperties {
Properties prop;
public LoadProperties(){
try{
prop = new Properties();
File fis = new File("src/main/resources/config.properties");
FileInputStream input = new FileInputStream(fis);
prop.load(input);
}
catch(Exception e){
System.out.println("Error loading configuration file");
}
}
public String getUrl()
{
return prop.getProperty("Url");
}
public String getNavigator(){
return prop.getProperty("navigator");
}
public String searchData(){
return prop.getProperty("search");
}
}
config.properties文件:
navigator = "firefox"
Url = "https://www.google.com"
search = "cucumber spring selenium"
非常重要的是,我正在使用 API WebdriverManager(作者 Boni Garcia),因为这样就没有必要将每个浏览器的驱动程序放在一个文件夹中并下载它们。
package hooks;
import com.codeborne.selenide.WebDriverRunner;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.opera.OperaDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
@Component
public class WebDriverManager {
private WebDriver webDriver;
public WebDriver getDriver() {
return webDriver;
}
@Bean(destroyMethod = "quit")
public WebDriver getWebDriver(){
String currentWebDriver = System.getProperty("browser", "");
switch(currentWebDriver) {
case ("chrome"):
System.setProperty("webdriver.chrome.driver", "chromedriver.exe");
webDriver = new ChromeDriver();
break;
case ("firefox"):
System.setProperty("webdriver.gecko.driver", "geckodriver.exe");
FirefoxOptions firefoxOptions = new FirefoxOptions();
firefoxOptions.setCapability("marionette", true);
webDriver = new FirefoxDriver(firefoxOptions);
break;
case ("chromeHeadless"):
System.setProperty("webdriver.chrome.driver", "chromedriver.exe");
ChromeOptions chromeHeadless = new ChromeOptions();
chromeHeadless.setHeadless(true);
webDriver = new ChromeDriver(chromeHeadless);
break;
case ("iexplorer"):
System.setProperty("webdriver.ie.driver", "IEDriverServer.exe");
DesiredCapabilities capabilitiesIE = DesiredCapabilities.internetExplorer();
capabilitiesIE.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
webDriver = new InternetExplorerDriver(capabilitiesIE);
break;
case ("edge"):
System.setProperty("webdriver.edge.driver", "MicrosoftWebDriver.exe");
webDriver = new EdgeDriver();
break;
case ("opera"):
System.setProperty("webdriver.opera.driver", "operadriver.exe");
webDriver = new OperaDriver();
break;
default:
System.getProperty("browser", "chrome");
System.setProperty("webdriver.chrome.driver", "chromedriver.exe");
webDriver = new ChromeDriver();
break;
}
//Using Selenide driver:
WebDriverRunner.setWebDriver(webDriver);
return webDriver;
}
@PreDestroy
public void closeSession(){
getDriver().manage().deleteAllCookies();
getDriver().close();
getDriver().quit();
}
}
另一方面,我正在使用Page Factory,这是用于使用的通用基本页面:
package pages;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public abstract class BasePage {
private WebDriver driver ;
private static final Logger log = Logger.getLogger(BasePage.class.getName());
//Constructor
public BasePage(WebDriver driver){
this.driver = driver;
this.driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
this.driver.manage().window().maximize();
this.driver.getTitle();
log.info("______________________Starting tests______________________");
}
public String getPageTitle(){
return driver.getTitle();
}
public Logger getLOG() {
return log;
}
public void navigate(String Url){
driver.get(Url);
}
/*
public abstract boolean isAt();
*/
//Methods
protected void waitForElementClickable(WebElement element){
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.elementToBeClickable(element));
}
protected void waitForElementVisible(WebElement element){
WebDriverWait wait = new WebDriverWait(driver, 15);
wait.until(ExpectedConditions.visibilityOf(element));
}
protected void setTextAs(WebElement element, String text) throws IOException {
waitForElementClickable(element);
element.sendKeys((text));
}
protected void clearElement(WebElement element){
waitForElementClickable(element);
element.clear();
}
protected void clickElement(WebElement element){
waitForElementClickable(element);
element.click();
}
protected void clickElementByJavascriptExecutor(String xpath){
WebElement element=driver.findElement(By.xpath(xpath));
JavascriptExecutor ex=(JavascriptExecutor)driver;
ex.executeScript("arguments[0].click()", element);
}
protected void clickLink(WebElement element){
waitForElementVisible(element);
element.click();
}
protected String getText(WebElement element){
waitForElementVisible(element);
return element.getText();
}
protected void freeze(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们不要忘记测试运行器类(testNGCucumberRunner)
package runners;
import cucumber.api.CucumberOptions;
import cucumber.api.testng.CucumberFeatureWrapper;
import cucumber.api.testng.PickleEventWrapper;
import cucumber.api.testng.TestNGCucumberRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@CucumberOptions(
plugin = {"pretty","json:target/report/cucumber2.json", "html:target/cucumber-reports"},
strict = true,
features = {"src/test/java/features"},
tags = {"@test"},
glue = {"steps"}
)
public class testNGCucumberRunner {
private static final Logger LOG = LoggerFactory.getLogger(runners.testNGCucumberRunner.class);
private TestNGCucumberRunner testNGCucumberRunner;
@BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
@Test(groups = "Cucumber", description = "Runs Cucumber Feature", dataProvider = "scenarios")
public void scenario(PickleEventWrapper pickleEvent, CucumberFeatureWrapper cucumberFeature) throws Throwable {
LOG.info("STARTING THIS FEATURE >>>>>" + cucumberFeature + "<<<<<<<<<<<<<<<<<<<<<<<<<<");
testNGCucumberRunner.runScenario(pickleEvent.getPickleEvent());
}
@DataProvider
public Object[][] scenarios() {
return testNGCucumberRunner.provideScenarios();
}
@AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
这里还有 Hooks 类(ServiceHooks)
package hooks;
import cucumber.api.Scenario;
import cucumber.api.java.After;
import cucumber.api.java.Before;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.codeborne.selenide.WebDriverRunner.getWebDriver;
public class ServiceHooks {
private static final Logger log = LoggerFactory.getLogger(ServiceHooks.class);
private WebDriver webDriver;
@Before
public void setUpBrowser(Scenario scenario) throws Exception {
log.info("****************************************************************************************");
log.info("$$$$$$$$$$$$$$$$$$$$$ Start of execution of scenario with name: " + scenario.getName() + " $$$$$$$$$$$$$$$$$$$$$$$");
log.info("****************************************************************************************");
if (webDriver == null) {
log.error("WebDriver did not initialize correctly");
}
}
@After
public void tearDown(Scenario scenario) throws Exception {
WebDriver driver = getWebDriver();
log.info("****************************************************************************************");
log.info("$$$$$$$$$$$$$$$$$$$$$ Completion of execution of scenario with name: " + scenario.getName() + " $$$$$$$$$$$$$$$$$$$$$$$");
log.info("****************************************************************************************");
if (scenario.isFailed()) {
log.info("****************************************************************************************");
log.info("$$$$$$$$$$$$$$$$$$$$$ Failing scenario with name: " + scenario.getName() + " $$$$$$$$$$$$$$$$$$$$$$$");
log.info("****************************************************************************************");
try {
byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
} catch (WebDriverException somePlatformsDontSupportScreenshots) {
System.err.println(somePlatformsDontSupportScreenshots.getMessage());
}
}
driver.quit();
log.info("Driver closed");
}
}
我的 POM 不能在这里忘记:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>automation</groupId>
<artifactId>selenium_cucumber</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<maven.compiler.version>3.7.0</maven.compiler.version>
<cucumber.version>3.0.2</cucumber.version>
<selenium.version>3.12.0</selenium.version>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-core</artifactId>
<version>${cucumber.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-java8 -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java8</artifactId>
<version>${cucumber.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>${cucumber.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-testng -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>${cucumber.version}</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.codeborne/selenide -->
<dependency>
<groupId>com.codeborne</groupId>
<artifactId>selenide</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
我的 testng.xml 文件是下一个:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Google Suite" verbose="1" thread-count="1" parallel="tests" configfailurepolicy="continue">
<test name="Google UI automation">
<classes>
<class name="runners.testNGCucumberRunner">
<methods>
<include name="scenario"/>
</methods>
</class>
</classes>
</test>
</suite>
与之前命名的功能相关的页面是下一个:
package pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import java.io.IOException;
public class Page_First extends BasePage{
/////////////Constructor/////////////
public Page_First(WebDriver driver) {
super(driver);
}
//////////////////////////////////////////////WEB ELEMENTS//////////////////////////////////////////////////////////
@FindBy(name = "q")
private WebElement searchText;
@FindBy(name="btnK")
private WebElement searchButton;
//////////////////////////////////////////////BASE METHODS//////////////////////////////////////////////////////////
public void search(String search) throws IOException {
setTextAs(searchText, search);
}
public void enterButton (){
clickElement(searchButton);
}
}
与此相关的步骤定义类是下一个:
package steps;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import pages.BasePage;
import pages.Page_First;
import utilities.LoadProperties;
import java.io.IOException;
import java.util.logging.Logger;
public class Step_First {
private WebDriver driver;
private BasePage basepage;
private Page_First page_first = PageFactory.initElements(driver, Page_First.class);
private static final java.util.logging.Logger log = Logger.getLogger(Page_First.class.getName());
LoadProperties load = new LoadProperties();
public Step_First(WebDriver driver){
this.driver = driver;
}
@Given("I go to {string} Google")
public void I_go_to_Google (String Url) {
load.getUrl();
// basepage.navigate(Url);
}
@When("I query for {string} cucumber spring selenium")
public void I_query_for_cucumber_spring_selenium (String search) throws IOException {
page_first.search(search);
}
@When("click search")
public void click_search(){
page_first.enterButton();
}
@Then("google page title should become the first page")
public void google_page_title_should_become_the_first_page() {
log.info("All OK");
}
}
在所有这些信息和类以及更多类之后,我真的不知道我在哪里失败了,因为当我从 IDE (IntelliJ IDEA)运行testNGCucumberRunner类时,我在控制台中遇到了下一个错误:
@test
Scenario: Search google.com to verify google search is working # src/test/java/features/first.feature:6
Given I go to "<Url>" Google # Step_First.I_go_to_Google(String)
org.picocontainer.injectors.AbstractInjector$UnsatisfiableDependenciesException: steps.Step_First has unsatisfied dependency 'interface org.openqa.selenium.WebDriver' for constructor 'public steps.Step_First(org.openqa.selenium.WebDriver)' from org.picocontainer.DefaultPicoContainer@4c12331b:1<|
at org.picocontainer.injectors.ConstructorInjector.getGreediestSatisfiableConstructor(ConstructorInjector.java:191)
at org.picocontainer.injectors.ConstructorInjector.getGreediestSatisfiableConstructor(ConstructorInjector.java:110)
at org.picocontainer.injectors.ConstructorInjector.access$100(ConstructorInjector.java:51)
at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:331)
at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:270)
at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:364)
at org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.getComponentInstance(AbstractInjectionFactory.java:56)
at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:64)
at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:91)
at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:699)
at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:647)
at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:678)
at cucumber.runtime.java.picocontainer.PicoFactory.getInstance(PicoFactory.java:40)
at cucumber.runtime.java.JavaStepDefinition.execute(JavaStepDefinition.java:48)
at cucumber.runtime.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:50)
at cucumber.runner.TestStep.executeStep(TestStep.java:55)
at cucumber.runner.TestStep.run(TestStep.java:42)
at cucumber.runner.PickleStepTestStep.run(PickleStepTestStep.java:53)
at cucumber.runner.TestCase.run(TestCase.java:47)
at cucumber.runner.Runner.runPickle(Runner.java:44)
at cucumber.api.testng.TestNGCucumberRunner.runScenario(TestNGCucumberRunner.java:56)
at runners.testNGCucumberRunner.scenario(testNGCucumberRunner.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
at org.testng.SuiteRunner.run(SuiteRunner.java:364)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
...
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
at ✽.I go to "<Url>" Google (src/test/java/features/first.feature:8)
我认为与picocontainer依赖相关(包含在我的 POM 中),但我无法解决失败的问题。
Failed scenarios:
src/test/java/features/first.feature:6 # Search google.com to verify google search is working
1 Scenarios (1 failed)
4 Steps (1 failed, 3 skipped)
0m0.071s
org.picocontainer.injectors.AbstractInjector$UnsatisfiableDependenciesException: steps.Step_First has unsatisfied dependency 'interface org.openqa.selenium.WebDriver' for constructor 'public steps.Step_First(org.openqa.selenium.WebDriver)' from org.picocontainer.DefaultPicoContainer@4c12331b:1<|
at org.picocontainer.injectors.ConstructorInjector.getGreediestSatisfiableConstructor(ConstructorInjector.java:191)
at org.picocontainer.injectors.ConstructorInjector.getGreediestSatisfiableConstructor(ConstructorInjector.java:110)
at org.picocontainer.injectors.ConstructorInjector.access$100(ConstructorInjector.java:51)
at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:331)
at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:270)
at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:364)
at org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.getComponentInstance(AbstractInjectionFactory.java:56)
at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:64)
at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:91)
at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:699)
at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:647)
at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:678)
at cucumber.runtime.java.picocontainer.PicoFactory.getInstance(PicoFactory.java:40)
at cucumber.runtime.java.JavaStepDefinition.execute(JavaStepDefinition.java:48)
at cucumber.runtime.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:50)
...
at cucumber.api.testng.TestNGCucumberRunner.runScenario(TestNGCucumberRunner.java:56)
at runners.testNGCucumberRunner.scenario(testNGCucumberRunner.java:33)
...
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
at ✽.I go to "<Url>" Google (src/test/java/features/first.feature:8)
===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 1
===============================================
有人可以帮我吗?提前致谢。