4

I want to migrate existing Selenium-Tests based on JUnit 4 to JUnit 5. For this purpose, I want to make use of Selenium-Jupiter.

One requirement, that the tests must fulfill is, to be able to switch the WebDriver implementation at runtime - based on the execution environment - using one common base class for all tests:

  • When executed on a developer machine (Windows 10), a FirefoxDriver should be used, launching a locally installed Firefox.
  • In the CI-environment (CentOS 7), a RemoteWebDriver should be used, delegating test execution to a Selenium Grid

I already tried to configure a "Generic WebDriver" according to documentation, but I don't know how to achieve this for a RemoteWebDriver ("Example 2" should make things clearer).

With regards to content this stackoverflow-posting is quite related, but does not yield a satisfying answer.

UPDATE: I solved the problem by myself. See "Example 3"...


Hey Boni, maybe you want to update the Selenium Jupiter documentation a bit. At least I had some problems, figuring it out... oh, and thanks a lot for the awesome work you have done with Selenium Jupiter (and the underlying WebDriverManger).


Example 1: What works, but is not elegant

@ExtendWith(SeleniumExtension.class)
class MyTest {

  @EnabledIfSystemProperty(named = "os.name", matches = "Windows 10")
  @Test
  void test_executed_with_local_firefox(FirefoxDriver ffDriver) {
     executeTestCaseWith(ffDriver);
  }

  @EnabledIfSystemProperty(named = "os.name", matches = "Linux")
  @Test
  void test_executed_with_firefox_from_selenium_grid(@DriverUrl("http://...") RemoteWebDriver remoteDriver) {
     executeTestCaseWith(remoteDriver);
  }

  void executeTestCaseWith(WebDriver webDriver) {
     webDriver.get(...)
     Assert(...)
  }
}

The problems I see hee, are:

  1. Pretty verbose (tho' this might by handable with custom annotations)
  2. Each test class needs two @Test-methods for one logical/semantical test case
  3. The concrete WebDriver implementation is exposed, thus, allowing test authors to rely on WebDriver implementation detail (for example, Firefox profiles)

Example 2: What I would like to do, but doesn't work, yet

Base class, that does WebDriver configuration:

abstract class UiTest {

   @RegisterExtension
   static SeleniumExtension seleniumExtension = new SeleniumExtension();     

   @BeforeAll
   static void setUpOnce() {

     // This check is not the problem discussed here
     boolean isRunningInCiEnvironment = ...
     Browser firefox;

      if( ! isRunningInCiEnvironment ) {
        // This works
        firefox = BrowserBuilder.firefox().build();
      } else {
        // this does not exist yet!
        //firefox = BrowserBuilder.remoteWebDriver().url("http://...").build();

        // This executes, but does not behave as expected
        // The URL is pretty much ignored.
        firefox = BrowserBuilder.firefox().url("http://...").build();
      }

     seleniumExtension.addBrowsers(firefox);
   }       
}

In the test I would then only need to do sth. like this.

class MyTest extends UiTest {

  @Test
  void my_one_logic_test_case(WebDriver webDriver) {

     webDriver.get(...)
     Assert(...)        
  }
}

Note, how the type of the parameter is only WebDriver!

Example 3: My current solution

abstract class UiTest {

   @RegisterExtension
   static SeleniumExtension seleniumExtension = new SeleniumExtension();     

   @BeforeAll
   static void setUpOnce() {

     // This check is not the problem discussed here
     boolean isRunningInCiEnvironment = ...

      if( isRunningInCiEnvironment ) {
        // This is doing the trick!
        seleniumExtension.getConfig().setSeleniumServerUrl("http://...");
      }

     seleniumExtension.addBrowsers( BrowserBuilder.firefox().build() );
   }       
}

The concrete test can now be implemented as depicted in "Example 2".

  • For anyone who is also stumbling across the same problem, this is my **ugly workaround**: 1. In the base class (here: `UiTest`), I declare a String member, containing the Selenium Grid's URL and annotate it with `@DriverUrl` (this makes the tests pass in CI environment) 2. When executing a test locally, I _comment this annotation_, which leads to a Firefox being started on my local machine – Kai Hoelscher Feb 14 '19 at 08:15
  • The "ugly workaround" is not needed any more (see "Example 3"). – Kai Hoelscher Feb 15 '19 at 13:33
  • Did you resolve issue #2? Does each test class still need to have 2 methods to execute one test in 2 browsers? – Paulus Apr 17 '20 at 21:40
  • Yes, that's still the case...but note that this has also advantages: Like having explicit test results. – Kai Hoelscher Apr 24 '20 at 15:06

1 Answers1

0

You can use @TestTemplate and leverage browser configuration using two browsers.json files.

  1. Write your test annotated with @TestTemplate:
class MyTest {

  @TestTemplate
  void my_logic_test_case(WebDriver webDriver) {
    // ...       
  }
}
  1. add a browsers.json to your classpath (Default, use local Firefox installation):
{
  "browsers": [
    [
      {
        "type": "firefox"
      }
    ]
  ]
}
  1. add a browsers-ci.json to your classpath (not default, uses Selenium Grid / RemoteWebDriver):
{
  "browsers": [
    [
      {
        "type": "firefox",
        "capabilities": {
          "browserName": "firefox"
        },
        "arguments": [
          "--headless"
        ],
        "remoteUrl": "https://mygrid.com/wd/hub"
      }
    ]
  ]
}

Now, you can run the test locally and it will use a local Firefox. If you run your tests on your CI environment, configure your CI script to set the system property sel.jup.browser.template.json.file to point to browsers-ci.json.

For example, if you use Maven to run your Selenium-Jupiter tests:

mvn test -Dsel.jup.browser.template.json.file=classpath:browsers-ci.json
eidottermihi
  • 212
  • 1
  • 11