Search code examples
spring-bootseleniumselenium-webdriverjunit5selenium-jupiter

Selenium-Jupiter generic webdriver settings not applied


I want to use Selenium-Jupiter in my Spring Boot Project but my requirement is to switch the WebDriver implementation at runtime - based on the execution environment - using one common base class for all tests.

I already checked this post and this one, but I couldn't get it to work.

Stacktrace:

  2020-09-17 23:17:47.196  INFO 14092 --- [    Test worker] d[....path....].SignupPageTest  : Started SignupPageTest in 26.133 seconds (JVM running for 29.179)
    2020-09-17 23:17:47.498  WARN 14092 --- [    Test worker] i.g.bonigarcia.seljup.SeleniumExtension  : Driver handler for context id [engine:junit-jupiter]/[class:de,[....path....].SignupPageTest]/[method:verifyLogInPage(org.openqa.selenium.WebDriver)] not found

    Failed to resolve parameter [org.openqa.selenium.WebDriver genDriver] in method [public void de.[....path....].SignupPageTest.verifyLogInPage(org.openqa.selenium.WebDriver) throws java.lang.Exception]: org/glassfish/hk2/api/MultiException
    org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [org.openqa.selenium.WebDriver genDriver] in method [public void de.[....path....].SignupPageTest.verifyLogInPage(org.openqa.selenium.WebDriver) throws java.lang.Exception]: org/glassfish/hk2/api/MultiException
        at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:239)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:183)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:144)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:96)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
        at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
        at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
        at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
        at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
        at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
        at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
        at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
        at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:564)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
        at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
        at com.sun.proxy.$Proxy5.stop(Unknown Source)
        at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:133)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:564)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
        at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
        at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
        at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
        at java.base/java.lang.Thread.run(Thread.java:832)
    Caused by: java.lang.NoClassDefFoundError: org/glassfish/hk2/api/MultiException
        at io.github.bonigarcia.seljup.DockerService.<init>(DockerService.java:79)
        at io.github.bonigarcia.seljup.SeleniumExtension.initHandlerForDocker(SeleniumExtension.java:381)
        at io.github.bonigarcia.seljup.SeleniumExtension.getDriverHandler(SeleniumExtension.java:242)
        at io.github.bonigarcia.seljup.SeleniumExtension.resolveParameter(SeleniumExtension.java:220)
        at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:216)
        ... 73 more
    Caused by: java.lang.ClassNotFoundException: org.glassfish.hk2.api.MultiException
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
        ... 78 more
    
    de.[....path....].SignupPageTest > verifyLogInPage(WebDriver) FAILED
        org.junit.jupiter.api.extension.ParameterResolutionException at ExecutableInvoker.java:239
            Caused by: java.lang.NoClassDefFoundError at DockerService.java:79
                Caused by: java.lang.ClassNotFoundException at BuiltinClassLoader.java:602

Just in advance with chrome-driver everything works fine, but not with the generic webdriver.

I use Java14 and created an additional source set for the selenium tests called "endToEndTest"
Here is everything related from the gradle.build:

sourceCompatibility = 14

sourceSets {
    endToEndTest {
        java.srcDir('src/end-to-end-test/java')
        resources.srcDir file('src/end-to-end-tests/resources')
        compileClasspath += sourceSets.main.output + configurations.testRuntime
        runtimeClasspath += output + compileClasspath
    }
}

configurations {
    endToEndTestImplementation.extendsFrom testImplementation
    endToEndTestRuntimeOnly.extendsFrom testRuntimeOnly
}

task endToEndTest(type: Test) {
    testClassesDirs = sourceSets.endToEndTest.output.classesDirs
    classpath = sourceSets.endToEndTest.runtimeClasspath
    useJUnitPlatform ()
}

endToEndTestImplementation('io.github.bonigarcia:selenium-jupiter:3.3.5')

I have a base class from which every other test class is supposed to extend. It takes care of starting the web environment etc.

Baseclass

@ExtendWith({SpringExtension.class})
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SeleniumBaseTestEnvironment {

  @RegisterExtension
  static SeleniumExtension seleniumJupiter = new SeleniumExtension();

  @LocalServerPort
  protected int serverPort;

  @BeforeAll
  public static void beforeAll() {
    //both seems to have no impact
    seleniumJupiter.getConfig().setDefaultBrowser("chrome");
    seleniumJupiter.addBrowsers(BrowserBuilder.chrome().build());
  }

  protected String getUrl(String address) {
    return "http://localhost:" + serverPort + address;
  }
}

Testclass It works just fine if I replace WebDriver with ChromeDriver. But I need the generic driver.

public class SignupPageTest extends SeleniumBaseTestEnvironment {

  @Test
  public void verifyLogInPage(WebDriver genDriver) throws Exception {
    genDriver.get(getUrl("/"));
    List<WebElement> cardTitleList = genDriver.findElements(By.className("card-title"));
    List<WebElement> navbar = genDriver.findElements(By.className("navbar-toggler collapsed"));
    assertEquals(cardTitleList.get(0).getText(), "Login");
    assertTrue(navbar.isEmpty());

  }
}

It seems like my settings regarding the default browser are not applied. Other setting like the ones for screenshots work just fine.


Solution

  • As per my research adding browsers to Selenium Extension works only for the Test Templates feature in the Selenium Jupiter.

    So using Test Templates following code should work:

        @ExtendWith({SpringExtension.class})
        @SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
        public class SeleniumBaseTestEnvironment {
        
          @RegisterExtension
          static SeleniumExtension seleniumJupiter = new SeleniumExtension();
        
          @LocalServerPort
          protected int serverPort;
        
          @BeforeAll
          public static void beforeAll() {
            String browsersList = System.getProperty("prop.browsers.list");
                List<String> browsers = Arrays.asList(browsersList.split(","));
            
            if (browsers.contains("chrome")) {
            seleniumJupiter.addBrowsers(BrowserBuilder.chrome().build()); 
            }
            if (browsers.contains("firefox")) {
                seleniumJupiter.addBrowsers(BrowserBuilder.firefox().build());
            }
            //----// All browsers you need
        
          }
        
          protected String getUrl(String address) {
            return "http://localhost:" + serverPort + address;
          }
    
    
    public class SignupPageTest extends SeleniumBaseTestEnvironment {
    
        @TestTemplate
        public void verifyLogInPage(WebDriver genDriver) throws Exception {
            genDriver.get(getUrl("/"));
            List<WebElement> cardTitleList = genDriver.findElements(By.className("card-title"));
            List<WebElement> navbar = genDriver.findElements(By.className("navbar-toggler collapsed"));
            assertEquals(cardTitleList.get(0).getText(), "Login");
            assertTrue(navbar.isEmpty());
    
        }
    }
    

    Please note:

    1. In my example I am using VM option -Dprop.browsers.list=chrome,firefox in order to provide multiple browsers into the test.
    2. Annotation @TestTemplate should be placed for each method of all subclasses of SeleniumBaseTestEnvironment.(You can create your own class level annotation to work around it).
    3. In order to switch Webdriver implementation you can setup a spring configuration for each environment that will provide an appropriate list of drivers.