测试前在 Cucumber-JVM 中初始化 WebDriver 作为依赖注入

Initialise WebDriver as dependency injection in Cucumber-JVM before test

我是 Cucumber-JVM 的新手,之前我曾经创建一个 BaseTest class,在某些 @BeforeMethod 中初始化 WebDriver,然后从 BaseTest class 扩展所有其他测试 类 ],因此 WebDriver 初始化在代码库中被管理一次,并且 WebDriver 初始化发生在每次测试之前,这是应该的。使用 Cucumber-JVM,我知道这个想法是在一些 class 中初始化 WebDriver,用 @ScenarioScoped 注释标记这个 class,然后在带有 @Inject 注释的 StepDefinitions 中使用这个 class 的实例 -这样我就可以访问创建的 WebDriver。我尝试了不同的选择,但没有成功。我按照这些指南中的步骤操作,但每次都会遇到类似的错误: http://www.thinkcode.se/blog/2017/08/16/sharing-state-between-steps-in-cucumberjvm-using-guice https://codoid.com/automation-testing/an-all-inclusive-guide-to-achieve-cucumber-dependency-injection-using-guice/

我在下面附上我的文件和当前状态,希望有人能指导我正确的方法。

[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.813 s <<< FAILURE! - in TestSuite
[ERROR] wiki.runners.MainPageRunnerTest.runScenario["Open main page from home page in English locale", "Optional[Open main page]"](1)  Time elapsed: 0.643 s  <<< FAILURE!
com.google.inject.ProvisionException: 
Unable to provision, see the following errors:

1) [Guice/ErrorInjectingConstructor]: NoSuchMethodError: 'ImmutableMap ImmutableMap.of(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)'
  at wiki.World.<init>(World.java:10)
  at wiki.World.class(World.java:10)
  at OpenMainPageDefinitions.world(OpenMainPageDefinitions.java:15)
      \_ for field world
  while locating OpenMainPageDefinitions

Learn more:
  https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR

1 error

======================
Full classname legend:
======================
ImmutableMap:            "com.google.common.collect.ImmutableMap"
OpenMainPageDefinitions: "wiki.definitions.OpenMainPageDefinitions"
========================
End of classname legend:
========================

        at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:251)
        at com.google.inject.internal.InjectorImpl.get(InjectorImpl.java:1104)
        at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1139)
        at io.cucumber.guice.GuiceFactory.getInstance(GuiceFactory.java:83)
        at io.cucumber.java.AbstractGlueDefinition.invokeMethod(AbstractGlueDefinition.java:47)
        at io.cucumber.java.JavaStepDefinition.execute(JavaStepDefinition.java:29)
        at io.cucumber.core.runner.CoreStepDefinition.execute(CoreStepDefinition.java:66)
        at io.cucumber.core.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:63)
        at io.cucumber.core.runner.ExecutionMode.execute(ExecutionMode.java:10)
        at io.cucumber.core.runner.TestStep.executeStep(TestStep.java:86)
        at io.cucumber.core.runner.TestStep.run(TestStep.java:57)
        at io.cucumber.core.runner.PickleStepTestStep.run(PickleStepTestStep.java:51)
        at io.cucumber.core.runner.TestCase.run(TestCase.java:95)
        at io.cucumber.core.runner.Runner.runPickle(Runner.java:75)
        at io.cucumber.testng.TestNGCucumberRunner.lambda$runScenario(TestNGCucumberRunner.java:132)
        at io.cucumber.core.runtime.CucumberExecutionContext.lambda$runTestCase(CucumberExecutionContext.java:151)
        at io.cucumber.core.runtime.RethrowingThrowableCollector.executeAndThrow(RethrowingThrowableCollector.java:23)
        at io.cucumber.core.runtime.CucumberExecutionContext.runTestCase(CucumberExecutionContext.java:151)
        at io.cucumber.testng.TestNGCucumberRunner.runScenario(TestNGCucumberRunner.java:129)
        at io.cucumber.testng.AbstractTestNGCucumberTests.runScenario(AbstractTestNGCucumberTests.java:35)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:135)
        at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:673)
        at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:220)
        at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
        at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:945)
        at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:193)
        at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
        at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.testng.TestRunner.privateRun(TestRunner.java:808)
        at org.testng.TestRunner.run(TestRunner.java:603)
        at org.testng.SuiteRunner.runTest(SuiteRunner.java:429)
        at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:423)
        at org.testng.SuiteRunner.privateRun(SuiteRunner.java:383)
        at org.testng.SuiteRunner.run(SuiteRunner.java:326)
        at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
        at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
        at org.testng.TestNG.runSuitesSequentially(TestNG.java:1249)
        at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
        at org.testng.TestNG.runSuites(TestNG.java:1092)
        at org.testng.TestNG.run(TestNG.java:1060)
        at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:324)
        at org.apache.maven.surefire.testng.TestNGXmlTestSuite.execute(TestNGXmlTestSuite.java:74)
        at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:123)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:456)
        at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:169)
        at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:595)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:581)
Caused by: java.lang.NoSuchMethodError: 'com.google.common.collect.ImmutableMap com.google.common.collect.ImmutableMap.of(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object)'
        at org.openqa.selenium.chrome.AddHasCasting.getAdditionalCommands(AddHasCasting.java:38)
        at org.openqa.selenium.chrome.ChromeDriver$ChromeDriverCommandExecutor.getExtraCommands(ChromeDriver.java:123)
        at org.openqa.selenium.chrome.ChromeDriver$ChromeDriverCommandExecutor.<init>(ChromeDriver.java:118)
        at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:106)
        at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:93)
        at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:82)
        at wiki.WebDriverManager.<init>(WebDriverManager.java:16)
        at wiki.World.<init>(World.java:11)
        at wiki.World$$FastClassByGuice$467.GUICE$TRAMPOLINE(<generated>)
        at wiki.World$$FastClassByGuice$467.apply(<generated>)
        at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82)
        at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
        at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
        at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
        at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
        at io.cucumber.guice.SequentialScenarioScope.lambda$scope[=10=](SequentialScenarioScope.java:40)
        at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:45)
        at com.google.inject.internal.SingleFieldInjector.inject(SingleFieldInjector.java:50)
        at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:146)
        at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:124)
        at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91)
        at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300)
        at com.google.inject.internal.InjectorImpl.get(InjectorImpl.java:1101)
        ... 50 more

[INFO] 
[INFO] Results:
[INFO] 
[ERROR] Failures: 
[ERROR]   MainPageRunnerTest>AbstractTestNGCucumberTests.runScenario:35 » Provision Unable to provision, see the following errors:

1) [Guice/ErrorInjectingConstructor]: NoSuchMethodError: 'ImmutableMap ImmutableMap.of(Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object, Object)'
  at wiki.World.<init>(World.java:10)
  at wiki.World.class(World.java:10)
  at OpenMainPageDefinitions.world(OpenMainPageDefinitions.java:15)
      \_ for field world
  while locating OpenMainPageDefinitions

Learn more:
  https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR

1 error

======================
Full classname legend:
======================
ImmutableMap:            "com.google.common.collect.ImmutableMap"
OpenMainPageDefinitions: "wiki.definitions.OpenMainPageDefinitions"
========================
End of classname legend:
========================

[INFO] 
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

OpenMainPageDefinitions.java:

package wiki.definitions;

import com.google.inject.Inject;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import wiki.World;
import wiki.pages.EnMainPage;
import wiki.pages.HomePage;

public class OpenMainPageDefinitions {

    @Inject World world;
    public HomePage homePage;
    public EnMainPage enMainPage;

    @Given("Home page is opened")
    public void homePageIsOpened() {
        homePage = new HomePage(world.manager.get());
        homePage.open().assertCentralFeaturedLogoIsDisplayed();
    }

    @When("User clicks on Main Page Link")
    public void userClicksOnMainPageLink() {
        homePage.clickEnMainPageLink();
        enMainPage = new EnMainPage(world.manager.get());
    }

    @Then("Welcome block is displayed")
    public void welcomeBlockIsDisplayed() {
        enMainPage.assertWelcomeBlockIsDisplayed();
    }

}

World.java

package wiki;

import io.cucumber.guice.ScenarioScoped;

@ScenarioScoped
public class World {

    public WebDriverManager manager;

    public World() {
        manager = new WebDriverManager();
    }

}

WebDriverManager.java

package wiki;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

public class WebDriverManager {

    public String env;
    public ChromeOptions options;
    public WebDriver driver;

    public WebDriverManager() {
        env = getEnv();
        options = getChromeOptions();
        driver = new ChromeDriver(options);
    }

    public ChromeOptions getChromeOptions() {
        if(env.equals("CIRCLE_CI")) {
            return new ChromeOptions()
                    .addArguments("--disable-dev-shm-usage")
                    .addArguments("--headless")
                    .addArguments("--no-sandbox");
        }
        return new ChromeOptions();
    }

    public String getEnv() {
        String env = System.getenv("WIKI_ENV");
        return env == null ? "LOCAL" : env;
    }

    public WebDriver get() {
        return driver;
    }

}

pom.xml

<?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>org.example</groupId>
    <artifactId>cucumber-selenium-tests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
        <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
        <testng.version>7.5</testng.version>
        <selenium-java.version>4.1.3</selenium-java.version>
        <cucumber.version>7.2.3</cucumber.version>
        <google-inject.version>5.1.0</google-inject.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium-java.version}</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-testng</artifactId>
            <version>${cucumber.version}</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber.version}</version>
        </dependency>
        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-guice</artifactId>
            <version>${cucumber.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>${google-inject.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <systemPropertyVariables>
                        <webdriver.chrome.driver>/tmp/chromedriver/chromedriver</webdriver.chrome.driver>
                    </systemPropertyVariables>
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

如果您 运行 mvn dependency:tree -Dverbose 在项目根目录中,您将能够分析项目的依赖关系层次结构。

com.google.common.collect.ImmutableMapguava 库中分发。依赖分析表明,Selenium 使用版本 31.1 而 Guice 使用版本 30.1.

Guice 下的 Guava 更靠近树根,因此 Maven 考虑到 lib 越靠近根,在发生冲突时它的优先级越高。

一共有三种解法:

  1. 将 Selenium 版本降低到使用较低版本 Guava 的版本(最好是 30.1,但也可以接受主要版本匹配)
  2. 将 Guice 版本增加到使用更高版本 Guava 的版本(最好是 31.1,但也可以接受主要版本匹配)
  3. 隐式指定 guava 依赖项。将 31.1 版本依赖项添加到您的 pom.xml。它将绝对优先于其他人。并希望它向后兼容30.1,这样Guice逻辑就不会受到影响。