Docker-based Testing

Last modified by Marius Dumitru Florea on 2024/07/05 13:27

Usage

  • Based on TestContainers and uses Docker to execute the tests under various Databases, Servlet Engines and Browsers.
  • The only requirements for running these tests is to have Docker installed locally and to have the user under which you run your IDE and the Maven build be able to use the docker executable.
  • Tests are marked as docker-based tests with the @UITest annotation.
  • Tests get injected XWikiWebDriver, TestUtils, TestInfo, TestReference and TestLocalReference instances as test method parameters.
  • Test results is recording in a FLV file in the target directory.
  • A screenshot of the UI when the test is failing is also taken in the target directory.
  • When test execute it's possible to connect to the running VNC server and see the UI by using a VNC client and connect to the VNC URL printed in the console, e.g. vnc://vnc:secret@localhost:32936.
    • Note that if you want to debug and interact with the XWiki UI you should use a servletEngine other than the default Jetty Standalone one since otherwise the debug point in your IDE will prevent the XWiki code from executing and you'll see a spinning wheel until you step forward in your debugger. You can use for example servletEngine = ServletEngine.TOMCAT.
  • The version for the platform dependencies is specified using the platform.version property in the pom.xml file executing the tests.
  • The test configuration options can be accessed from tests by specifying a method parameter of type TestConfiguration (see examples below).
    • You can get access to the IP address/port to use to connect to XWiki from the host machine: testConfiguration.getServletEngine().getIP()/getPort()
    • You can get access to the IP address to use to connect to the host machine from inside XWiki: testConfiguration.getServletEngine().getHostIP()
    • You can get access to the IP address/port used internally by XWiki (for example to generate URLs): testConfiguration.getServletEngine().getInternalIP()/getInternalPort()
  • A special source for @ParameterizedTest is available to inject subwikis as input for a test

Configurations options

Note:

  • When passing as Java annotation attributes, in functional tests, the format is @UITest(<Option Name> = <Value>)
  • When passing the options as System Properties, the format is -Dxwiki.test.ui.<Option Name>=<Value>
    • Except for passing properties where the format is -Dxwiki.test.ui.properties.<Property name>=<Property Value>
    • Except for extraJARs, sshPorts & forbiddenServletEngines which cannot be set from System Properties.
    • Except for passing databaseCommands where the format is -Dxwiki.test.ui.database.commands.<Command name>=<Command Value>
  • System properties have priority over Java annotation attributes
  • The xwiki.test.ui.dependencies configuration value can only be passed as a system property.
Option NameDefault ValueValid ValuesDescription
browserfirefox
  • firefox (Browser.FIREFOX)
  • chrome (Browser.CHROME)
The browser used in the tests. Note that the version of the browser used is controlled by the version of Selenium that is defined in the pom.xml of xwiki-platform-test-docker.
databasehsqldb_embedded
  • mysql (Database.MYSQL)
  • postgresql (Database.POSTGRESQL)
  • hsqldb_embedded (Database.HSQLDB_EMBEDDED)
  • oracle DB (Database.ORACLE)
The database used in the tests. Available tags are those listed on the Dockerhub page for the database image used. Note: for Oracle, we have a custom image.
servletEnginejetty_standalone
  • tomcat (ServletEngine.TOMCAT)
  • jetty (ServletEngine.JETTY)
  • jetty_standalone (ServletEngine.JETTY_STANDALONE)
  • external (ServletEngine.EXTERNAL)
The Servlet Engine used in the tests. Use external to use your own started and alreayd provisioned XWiki instance.
verbosefalse
  • true
  • false
When active, displays more logs in the console (especially container startup logs). XWiki 16.2+ All debug logs are now shown when verbose is set to true.
debugfalse
  • true
  • false
When active, starts XWiki in debug mode when using the "Jetty Standalone" and Tomcat Servlet Engines (and starts it with suspend on, i.e. wait for the remote debugger to connect to it before progressing, to make it easy to debug XWiki's server side). You can then attach a remote debugger. Since Tomcat runs in a Docker container, you'll need to do a docker ps to find the local mapped port corresponding to port 5005 and then use localhost as the JVM host name. XWiki <16.2 Some debug logs were also shown only with this parameter set to true.
officefalse
  • true
  • false

When active it creates or gets a custom docker image based on the servlet engine image provided but with LibreOffice installed on it. The created image is named and tagged using the following scheme: xwiki-[servlet engine name]-office:[servlet engine tag].

The LibreOffice version used for the install is based on the Maven property libreoffice.version.

offlinefalse
  • true
  • false
When offline, the custom XWiki WAR generation and the XWiki provisioning are done solely from your local Maven repository. Otherwise, when artifacts are not present locally or newer SNAPSHOT versions are available, they'll be fetched from Maven remote repositories. Moreover no docker pull check is performed in offline mode.
databaseTaglatestAny Docker tag available from Dockerhub for the container imageVersion of the database to use. Isn't supported for HSQLDB Embedded since it doesn't run in a Docker container. See the latest supported version by XWiki.
servletEngineTagDefault tag version defined by TestContainersAny Docker tag available from Dockerhub for the container imageVersion of the Servlet Engine to use. Isn't supported for Jetty Standalone since it doesn't run in a Docker container. See the latest supported version by XWiki.
jdbcDriverVersionLatest version validated by the XWiki dev teamAny version for the specified database that is available on Maven Central, e.g. for MySQL the groupId/artifactId is mysql/mysql-connector-java. 
vnctrue
  • true
  • false
When active a VNC container is started to record a video of the tests and more generally to allow connecting to the UI running the tests. Useful when debugging.
propertiesNo default (empty)Check the *.vm files for the configuration files to see the list of possible values.Velocity properties that are applied when generating XWiki's configuration files: xwiki.cfg, xwiki.properties and hibernate.cfg.xml. Example: Tell XWiki that minification is off: -Dxwiki.test.ui.properties.xwikiPropertiesAdditionalProperties=debug.minify=false or @UITest(properties = { "xwikiPropertiesAdditionalProperties=debug.minify=false" }). Note that the xwikiCfgPlugins property will be merged with the default values unless you start the new property value with the ^ character.
extraJARsNo default (empty)Comma-separated list of <groupId>:<artifactId> or <groupId>:<artifactId>:<version>There are cases where the module being tested cannot be fully deployed as an extension into a running XWiki and it needs to deploy one or several JARs into WEB-INF/lib (as core extensions). One such example is when an extension includes a Hibernate HBM file, as right now only HBM files located in JARs in WEB-INF/lib are supported. Note that when the version is not specified the current POM's version is used.
sshPortsNo default (empty)List of integersThe list of ports that should be SSH-forwarded when connecting from a Docker container to the host (i.e. when using the host.testcontainers.internal. This is in addition to port 8080 which is always added. For example if you need XWiki to send a mail to a SMTP server running on port 3025 on the host, you should add port 3025 to the list.
profilesNo default (empty)Comma-separated list of Maven profile idsWhen specified the defined profiles are active when resolving dependencies from the current POM.
forbiddenServletEnginesNo default (empty)List of ServletEngines enumsList of Servlet Engines for which the tests will be skipped (usually because they'd fail on them).
databasePropertiesNo default (empty)See Docker documentation for the target imageList of database docker commands to use and that will override default commands (example of command character-set-server=utf8mb4 which will translate into --character-set-server=utf8mb4 at runtime).
dependenciesNo default (empty)Comma-separated list of Maven coordinatesList artifacts that will be provisioned in the running XWiki instance. When specified then current pom.xml dependencies are not used to find out what to provision. This can be used for example in cases when you don't want to use project dependencies in order to not draw older version of dependencies (such as when you need to run functional docker-based tests in a specific more recent vesion of XWiki than the one used to build the code). Accepted format is groupId:artifactId, groupId:artifactId:version and groupId:artifactId:type:version.
extensionOverridesNo default (empty)List Extension Manager overridesSee example below for the format.
resolveExtraJARsfalse
  • true
  • false
Specifies if the extra JAR versions must be resolved (when their versions are not specified), by checking the transitive list of dependencies in the current POM. Note that there are 2 limitations: resolving takes time and SNAPSHOT versions will be resolved to the latest published SNAPSHOT. This is why it's off by default. When false the current POM version will be used for the missing exyta JAR versions. The main use case for using true is in contrib extensions where the current POM version usually don't match extra JARs versions and you may not want to hardcode the version and thus you can let the docker test framework find it for you.
saveDatabaseDatafalse
  • true
  • false
By default, database data is not saved between test executions. Note that if you decide to save database data, they'll be saved under a docker user by Docker and you need your local user to be able to remove them if you want to clean your Maven target directory.
savePermanentDirectoryDatafalse
  • true
  • false
XWiki 14.5+ Allows to persist the XWiki permanent directory in the Maven target directory, after the test has finished executing. This can be useful for debugging purposes, especially when executing remotely without an IDE (on a CI agent for example).
wcagfalse
  • true
  • false
XWiki 15.2+ Enables automatic WCAG validation using axe-core while running integration tests. The checks may only fail the test suites but no individual test. See WCAG Testing for more information.
wcagStopOnErrortrue
  • true
  • false
XWiki 15.10.7+, 16.1.0+ Allows to ignore errors that might happen during WCAG validation step: by default errors are not ignored and exception are thrown that causes test failures. This property is only taken into account when wcag flag is set to true.
browserTaglatestAny Docker tag available from Dockerhub for the selenium browser container imageXWiki 16.3.0+ Allows to specify a specific image to use for the selenium browser container. Note that this option is given for debugging purpose only, as we want to run our tests with the latest version of browsers since this is what we support.
servletEngineNetworkAliases[]List of Docker network aliasesXWiki 15.10.12+, 16.4.1+, 16.6.0+ Allows to specify additional network aliases to use for the servlet engine Docker container. If the servlet engine runs directly on the Docker host then this is used to configure additional hosts on the browser Docker container. This is useful when you need to access the same XWiki instance using different domains, e.g. because you need to login with different XWiki users in the same browser instance (but different tabs). Use this along with MultiUserTestUtils that you can inject as parameter in your test methods.

Examples

Full Examples

Example 1: Basic test

@UITest
public class SeleniumTest
{
   @Test
   public void test(XWikiWebDriver driver, TestUtils setup)
   {
        driver.get("http://xwiki.org");
        assertThat(driver.getTitle(),
            containsString("XWiki - The Advanced Open Source Enterprise and Application Wiki"));
        driver.findElement(By.linkText("XWiki's concept")).click();
   }
}

Example 2: Choosing Container + DB + Browser and versions

@UITest(database = Database.MYSQL, databaseTag = "5", servletEngine = ServletEngine.TOMCAT, servletEngineTag = "8",
  browser = Browser.CHROME)
public class MenuIT
...
@UITest(database = Database.POSTGRESQL, databaseTag = "9", servletEngine = ServletEngine.JETTY,
  servletEngineTag = "9", browser = Browser.CHROME)
public class MenuIT
...
@UITest(database = Database.HSQLDB_EMBEDDED, servletEngine = ServletEngine.JETTY_STANDALONE,
  browser = Browser.FIREFOX)
public class MenuIT
...

Example 3: Test using Greenmail + Hibernate HBM

@UITest(sshPorts = {
       3025
   },
    properties = {
       "xwikiDbHbmCommonExtraMappings=mailsender.hbm.xml",
       // Pages created in the tests need to have PR since we ask for PR to send mails so we need to exclude them from
       // the PR checker.
       "xwikiPropertiesAdditionalProperties=test.prchecker.excludePattern=.*:MailIT\\..*"
   },
    extraJARs = {
       "org.xwiki.platform:xwiki-platform-mail-send-storage"
   }
)
public class MailIT
...

Example 4: Passing Test Configuration

@Test
public void verifyMail(TestUtils setup, XWikiWebDriver webDriver, TestConfiguration testConfiguration)
   throws Exception
{
   ....
}

Example 5: Specifying explictly dependencies

Example of deploying a 1.10-SNAPSHOT version of org.xwiki.contrib.latex:latex-export (and its dependencies) into a 11.2-SNAPSHOT version of XWiki. In this example the POM of org.xwiki.contrib.latex:latex-export depends on XWiki 10.2. Thus this example demonstrates running the tests in a more recent version of XWiki than the code was built on.

...
<parent>
 <groupId>org.xwiki.platform</groupId>
 <artifactId>xwiki-platform</artifactId>
 <version>11.2-SNAPSHOT</version>
</parent>
<groupId>org.xwiki.contrib.latex</groupId>
<artifactId>latex-test</artifactId>
<version>1.10-SNAPSHOT</version>
...
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-failsafe-plugin</artifactId>
 <configuration>
   <includes>
     <include>**/AllIT.java</include>
   </includes>
   <systemProperties>
     <xwiki.test.ui.dependencies>org.xwiki.contrib.latex:latex-export</xwiki.test.ui.dependencies>
   </systemProperties>
 </configuration>
</plugin>

Example 6: Passing extension overrides

The following tells the Extension Manager to use the existing core extension com.google.code.findbugs:annotations whenever the extension com.google.code.findbugs:jsr305 is requested.

@UITest(extensionOverrides = {
   @ExtensionOverride(
        extensionId = "com.google.code.findbugs:jsr305",
        overrides = {
           "features=com.google.code.findbugs:annotations"
       }
   )
})
...

Note that in the XWiki build, these extension overrides are defined in the top level XWiki Platform POM and for this specific case it's defined as:

...
<extensionOverride>
 <id>com.google.code.findbugs:jsr305</id>
 <features>com.google.code.findbugs:annotations</features>
</extensionOverride>
...

Another example:

...
extensionOverrides = {
 @ExtensionOverride(
    extensionId = "org.xwiki.platform:xwiki-platform-web",
    overrides = {
     // We set a default UI for the subwiki in the webapp, so that the Wiki Creation UI knows which extension
     // to install on a subwiki by default (which is something we test)
     // Otherwise the wiki creation form will display the flavor picker and the functional tests do not handle it.
     "properties=xwiki.extension.distribution.wikiui=org.xwiki.platform:xwiki-platform-wiki-ui-wiki"
   }
 )
}
...

Example 7: Execute a test on main and subwiki

It can be interresting to verify that a test scenario works both on main and subwiki to catch context related bugs or simply things which were designed too much with main wiki in mind. A @ParameterizedTest source is available to automate this.

The following example executes the test copyPage twice, once with the main wiki in input and the second time with an automatically created wiki as input and indicate that the extension org.xwiki.platform:xwiki-platform-index-tree-macro is required in the subwiki:

    @ParameterizedTest
   @WikisSource(extensions = {"org.xwiki.platform:xwiki-platform-index-tree-macro"})
   void copyPage(WikiReference wiki, TestLocalReference testReference, TestUtils setup, TestConfiguration testConfiguration) throws Exception
   {
     ....
   }

WikiReference must be the first parameter of the method

This tool relies on wiki creation job, so you will need to include the following modules to your instance:

    <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-wiki-creationjob</artifactId>
     <version>${project.version}</version>
   </dependency>
   <dependency>
     <groupId>org.xwiki.platform</groupId>
     <artifactId>xwiki-platform-wiki-template-default</artifactId>
     <version>${project.version}</version>
   </dependency>

Example 8: Login multiple users at the same time

XWiki 15.10.12+, 16.4.1+, 16.6.0+

The current approach to achieve this is to:

  • map the servlet engine running XWiki to multiple domains (Docker network aliases)
  • open multiple browser tabs
  • access XWiki using a different domain in each browser tab
  • log in with different users in different browser tabs

We implemented a helper to ease this:

@UITest(
  servletEngineNetworkAliases = MultipleUsersIT.XWIKI_ALIAS
)
class MultipleUsersIT
{
   public static final String XWIKI_ALIAS = "xwiki-alias";

   @Test
   @Order(1)
   void loginWithMultipleUsers(TestUtils setup, MultiUserTestUtils multiUserSetup)
   {
       // First tab.
       setup.createUserAndLogin("Alice", "foo");

       // Second tab.
       String secondTabHandle = multiUserSetup.openNewBrowserTab(XWIKI_ALIAS);
        setup.createUserAndLogin("Bob", "bar");

       // Back to first tab.
       multiUserSetup.switchToBrowserTab(multiUserSetup.getFirstTabHandle());

       // Finally, close the opened tabs, except for the first one.
       multiUserSetup.closeTabs();
   }
}

Best Practices

Scenarios

Always use scenarios and use JUnit5's @Order(...) annotation. For example:

@UITest
public class MenuIT
{
    
@Test
    
@Order(1)
    
public void verifyMenuInApplicationsIndex(TestUtils setup)
    
{
      
...
    
}

    
@Test
    
@Order(2)
    
public void verifyMenuCreationInLeftPanelWithCurrentWikiVisibility(TestUtils setup)
    
{
      
...
    
}

    
@Test
    
@Order(3)
    
public void verifyMenuIsAvailableInAdministration(TestUtils setup) throws Exception
    
{
    
...
  
}
}

Suites

Use JUnit5's @Nested annotation. For example:

@UITest
public class AllIT
{
   @Nested
   @DisplayName("Office Import Tests")
   class NestedOfficeImporterIT extends OfficeImporterIT
   {
   }

   @Nested
   @DisplayName("Office Export Tests")
   class NestedOfficeExportIT extends OfficeExportIT
   {
   }
}

XWiki 12.8+ When using nested tests, only configure any @UITest parameter in the nested tests themselves and not in the parent (e.g. at the AllIT level the @UITest annotation should be empty). This is because @UITest annotation parameters are automatically merged.

Test Reference

A lot of tests need to create a wiki page for testing. Example to do this:

@Test
public void showAndHideEditComments(TestUtils setup, TestReference reference) throws Exception
{
        ViewPage vp = setup.gotoPage(reference);
...

More generic way (more verbose but can useful in some cases) using JUnit5's TestInfo class:

public class MailIT
{
   private String testClassName;

   @BeforeEach
   public void setUp(TestInfo info)
   {
       this.testClassName = info.getTestClass().get().getSimpleName();
   }
 ...
}

Docker out of Docker

The XWiki's CI is using a Jenkins Agent Docker image to execute the job builds. Thus, the Docker-based functional tests spawn Docker containers (using TestContainers) that execute inside Docker. This is the pattern called DOOD (Docker out of Docker). See TC's best practice about this.

There are some consequences when writing tests for XWiki:

  • Never use volume mapping or it won't execute fine. This is because that would require the Jenkins Agent Docker container to have that volume mapping defined too and the mapping would need to use the exact host path and container path. So, in short, it won't work. Instead copy files that you need to have in the container. Adn copy file out if need be. Here's an example from the LaTeX extension on how to achieve this:
    try (GenericContainer container = new GenericContainer("blang/latex:ubuntu")) {
       // Note: we copy files instead of mounting volumes so that this can work when using DOOD
       // (Docker out of Docker).
       MountableFile mountableDirectory = MountableFile.forHostPath("target/latex");
        container.withCopyFileToContainer(mountableDirectory, "/data");

        container.withCommand("/bin/sh", "-c", "sleep infinity");
        container.withLogConsumer(new Slf4jLogConsumer(LOGGER));
        container.start();
        Container.ExecResult result = container.execInContainer("pdflatex", "-shell-escape", "index.tex");
        String stdout = result.getStdout();
        LOGGER.info(stdout);
        assertTrue(stdout.contains("Output written on index.pdf"));

        container.copyFileFromContainer("/data/index.pdf", "target/latex/index.pdf");
    }

Stdout/stderr validation errors

We have a check that verifies if functional tests output some invalid content to stdout/stderr. If your test contains such errors it'll fail and then you have 3 options:

  • It's a normal and expected output from the test (for ex the test verifies an error condition and it's expected it will raise a stack trace in the console). In this case add an expectation in the test. For example:
    public void wrongTemplate(LogCaptureConfiguration logCaptureConfiguration)
    {
       ...
        logCaptureConfiguration.registerExpected( "Possible break-in attempt!",
           "Error getting resource [null]");
    }
  • It's a non-expected error. You then have 2 sub-choices:
    • Fix the problem (the best and recommended solution!)
    • Increase the technical debt by adding an exclude. For example:
      logCaptureConfiguration.registerExcludes(
         "java.lang.IllegalStateException: Response is committed");

Test Resources

You might need test resources to be used in interaction with the wiki (e.g. to upload an attachment). Any test resources placed in src/test/resources is made automatically available to the browser container by mounting the dedicated volume mapped to target/test-classes. Then, in your test, you can use the dedicated method to get access to the files located in target/test-classes.

For example:

String resourceDirPath = testConfiguration.getBrowser().getTestResourcesPath();
File myResourceFile = new File(resourceDirPath, filename);

Code Resources

You might need custom test code deployed to the XWiki webapp for the test (to override some components for example). Any classes that land into target/classes will be copied to the webapp's WEB-INF/classes directory. Thus you can, for example, put sources in src/main/* to see it deployed in the webapp.

Test Logging

You might want to activate some debug logs specific to the test. This can be achieved by providing a logback-override.xml file in your src/test/resources directory of your Maven project.

Test Coverage

Activate support for Clover by setting the xwiki.test.ui.profiles property in the clover profile in the pom.xml file for the test.

Example:

  <profiles>
   <profile>
     <id>clover</id>
     <!-- Add the Clover JAR to the WAR so that it's available at runtime when XWiki executes.
           It's needed because instrumented jars in the WAR will call Clover APIs at runtime when they execute. -->

     <dependencies>
       <dependency>
         <groupId>org.openclover</groupId>
         <artifactId>clover</artifactId>
       </dependency>
     </dependencies>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-failsafe-plugin</artifactId>
           <configuration>
             <systemProperties combine.children="append">
               <!-- Tell the Docker-based test to activate the Clover profile so that the Clover JAR is added to
                     WEB-INF/lib -->

               <property>
                 <name>xwiki.test.ui.profiles</name>
                 <value>clover</value>
               </property>
             </systemProperties>
           </configuration>
         </plugin>
...

Existing XWiki instance

If you want to avoid the Docker-based tests to start and stop XWiki every time you re-run the tests (for example when you're developping/debugging something), you can tell the Docker Test framework to not provision an XWiki instance and to use your XWiki instance instead.

This is achieved by specifying @UITest(servletEngine = ServletEngine.EXTERNAL)
or passing the xwiki.test.ui.servletEngine=external system property.

A common use case is to debug a failing test. Here's what you can do:

## Run the test once to see if it fails and to generate the XWiki instance. This may take some time.
> mvn clean install -Dit.test=NavigationPanelIT
## Open a new console, look for the generated XWiki instance inside the target folder and start it.
../target/../jetty > start_xwiki.sh
## Modify the test as needed then go back to the initial console and compile the tests.
## Note that you may also need to build the page objects module if you change those too.
> mvn compiler:testCompile
## Then run the test again to see the changes. This should be way faster than the initial test run.
> mvn failsafe:integration-test -Dxwiki.test.ui.servletEngine=external -Dit.test=NavigationPanelIT

Run a single test

For example to run the NestedResetPasswordIT tests from an AllIT test class defined as:

@UITest
public class AllIT
{
   @Nested
   @DisplayName("Overall Administration UI")
   class NestedAdministrationIT extends AdministrationIT
   {
   }

   @Nested
   @DisplayName("Reset Password")
   class NestedResetPasswordIT extends ResetPasswordIT
   {
   }
...

You'd use:

mvn -Dit.test=ResetPasswordIT clean verify

Notes:

  • Using -Dit.test=*NestedResetPasswordIT* is not going to work well for some reason (we still need to debug this to understand why it doesn't work as expected).
  • Using -Dit.test=ResetPasswordIT makes the Maven Surefire plugin use the individual test instead of AllIT, even though the top level Commons POM says that only the AllIT class is included by default.

See also the Existing XWiki instance section.

Dynamic Configuration

Some tests may require a dynamic configuration. For example:

  • Active Installs tests require to set the activeinstalls2.pingURL configuration property in xwiki.properties dynamically since the Ping URL changes for each test (it's the URL to an ElasticSearch docker container with a mapped port that changes to avoid impacting other tests).
  • The client-side PDF Export tests also need to set a export.pdf.dockerNetwork configuration property in xwiki.properties dynamically for similar reasons.

To support these cases, it's possible to execute some Java code that will run before your tests execute and that will provide a TestConfiguration pushed in the JUnit5 Global Test Context, which will then be merged automatically by the XWiki Docker Test framework.

Example

public class DynamicTestConfigurationExtension implements BeforeAllCallback
{
   @Override
   public void beforeAll(ExtensionContext extensionContext)
   {
       // Get the Elasticsearch container set by XWikiElasticSearchExtension
       ExtensionContext.Store store = XWikiElasticSearchExtension.getStore(extensionContext);
        ElasticsearchContainer esContainer = (ElasticsearchContainer) store.get(ElasticsearchContainer.class);

       // Save a TestConfiguration in the global test context so that it's merged in XWikiDockerExtension.
-->     ExtensionContext.Store globalStore = extensionContext.getRoot().getStore(ExtensionContext.Namespace.GLOBAL);
        TestConfiguration configuration = new TestConfiguration();
        Properties properties = new Properties();
        String hostAddress = String.format("http://%s:%d", esContainer.getHost(), esContainer.getMappedPort(9200));
        properties.setProperty("xwikiPropertiesAdditionalProperties",
            String.format("activeinstalls2.pingURL=%s", hostAddress));
        configuration.setProperties(properties);
       // When the Servlet engine is running inside docker, Active Installs (thus running inside docker) will need
       // access the socket of the Elasticsearch server running on the host. Thus, we need to expose the port, see
       // https://www.testcontainers.org/features/networking/#exposing-host-ports-to-the-container.
       configuration.setSSHPorts(List.of(esContainer.getMappedPort(9200)));
-->     globalStore.put(TestConfiguration.class, configuration);
   }
}

And then:

@ExtendWith(XWikiElasticSearchExtension.class)
@ExtendWith(DynamicTestConfigurationExtension.class)
@UITest
public class AllIT
...

Ignore Browsers

XWiki 16.6+, 16.4.1+, 15.10.12+

If a test must not execute on a given browser or given browser version, it's possible to disable it using the @IgnoreBrowser annotation.

Here's an example:

@Test
@IgnoreBrowser(value = "firefox", reason = "Alert handling in Firefox currently isn't working, see also "
   + "https://jira.xwiki.org/browse/XWIKI-22282")
@Order(6)
codepublic void saveAndFormManipulation(TestUtils setup, TestReference reference)
{...}

Architecture

xwiki-testcontainers.png
See Vincent's blog for more details

CI Jobs

There are 3 CI jobs executing Docker-based functional tests:

  • Job 1: run all tests on latest versions of supported configs. Runs WCAG validation on the first configuration.
    WCAG validation is only done on one configuration because WCAG results are for the most part independant from the configuration and their validation is very time consuming.
  • Job 2: run smoke tests (ie a few tests only) on all supported configs.
  • Job 3: run smoke tests on unsupported configs (MySQL 8.x, Tomcat 9.x, etc) that we want to support in the future.

Configuration definitions and job sources are all here.

Firefox Version Used

If you need to know which version of Firefox is used during the Docker-based test execution, do the following:

  • Make sure you have the latest docker image used by TestContainers (replace the version by the version used by XWiki): docker pull selenium/standalone-firefox:latest (or docker run seleniarm/standalone-firefox:latest on ARM)
  • Start the container: docker run selenium/standalone-firefox:latest (or docker run seleniarm/standalone-firefox:latest on ARM). Note the container id.
  • Log into the running container: docker exec -it <container id> bash -l
  • Ask for the Firefox version: firefox --version

Example:

seluser@f57f7f7b93d6:/$ firefox --version
Mozilla Firefox 92.0.1
seluser@f57f7f7b93d6:/$

Get Connected