Search code examples
javaeclipsemavenjmockittycho

Using JMockit with Tycho


I have several Eclipse Plugin-Projects which I want to compile using Maven & Tycho. The project can be found at github. There you see three projects with their pom.xml files. With that I aim to run unit test using JMockit.

To start Maven, I switch into the maven.parent directory and execute mvn clean verify which produces the following output:

D:\maven-test\maventest.parent>mvn clean verify
[INFO] Scanning for projects...
[INFO] Computing target platform for MavenProject: maventest:maventest:1.0.0-SNAPSHOT @ D:\maven-test\maventest\pom.xml
[INFO] Adding repository ....censored....
[INFO] Adding repository ....censored....
[INFO] Resolving dependencies of MavenProject: maventest:maventest:1.0.0-SNAPSHOT @ D:\maven-test\maventest\pom.xml
[INFO] Resolving class path of MavenProject: maventest:maventest:1.0.0-SNAPSHOT @ D:\maven-test\maventest\pom.xml
[INFO] Computing target platform for MavenProject: maventest:test.maventest:1.0.0-SNAPSHOT @ D:\maven-test\test.maventest\pom.xml
[INFO] Resolving dependencies of MavenProject: maventest:test.maventest:1.0.0-SNAPSHOT @ D:\maven-test\test.maventest\pom.xml
[INFO] Resolving class path of MavenProject: maventest:test.maventest:1.0.0-SNAPSHOT @ D:\maven-test\test.maventest\pom.xml
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] maventest.parent
[INFO] maventest
[INFO] test.maventest
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maventest.parent 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maventest.parent ---
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maventest 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:build-qualifier (default-build-qualifier) @ maventest ---
[INFO] The project's OSGi version is 1.0.0.201512010744
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:validate-id (default-validate-id) @ maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:validate-version (default-validate-version) @ maventest ---
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ maventest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\maven-test\maventest\src\main\resources
[INFO]
[INFO] --- tycho-compiler-plugin:0.24.0:compile (default-compile) @ maventest ---
[INFO] Compiling 2 source files to D:\maven-test\maventest\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ maventest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\maven-test\maventest\src\test\resources
[INFO]
[INFO] --- target-platform-configuration:0.24.0:target-platform (default-target-platform) @ maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:package-plugin (default-package-plugin) @ maventest ---
[INFO] Building jar: D:\maven-test\maventest\target\maventest-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- tycho-p2-plugin:0.24.0:p2-metadata-default (default-p2-metadata-default) @ maventest ---
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building test.maventest 1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ test.maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:build-qualifier (default-build-qualifier) @ test.maventest ---
[INFO] The project's OSGi version is 1.0.0.201512010744
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:validate-id (default-validate-id) @ test.maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:validate-version (default-validate-version) @ test.maventest ---
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ test.maventest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\maven-test\test.maventest\src\main\resources
[INFO]
[INFO] --- tycho-compiler-plugin:0.24.0:compile (default-compile) @ test.maventest ---
[INFO] Compiling 1 source file to D:\maven-test\test.maventest\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ test.maventest ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\maven-test\test.maventest\src\test\resources
[INFO]
[INFO] --- target-platform-configuration:0.24.0:target-platform (default-target-platform) @ test.maventest ---
[INFO]
[INFO] --- tycho-packaging-plugin:0.24.0:package-plugin (default-package-plugin) @ test.maventest ---
[INFO] Building jar: D:\maven-test\test.maventest\target\test.maventest-1.0.0-SNAPSHOT.jar
[INFO]
[INFO] --- tycho-p2-plugin:0.24.0:p2-metadata-default (default-p2-metadata-default) @ test.maventest ---
[INFO]
[INFO] --- tycho-surefire-plugin:0.24.0:test (default-test) @ test.maventest ---
[INFO] Expected eclipse log file: D:\maven-test\test.maventest\target\work\data\.metadata\.log
[INFO] Command line:
        [C:\Program Files\Java\jdk8\jre\bin\java.exe, -Dosgi.noShutdown=false, -Dosgi.os=win32, -Dosgi.ws=win32, -Dosgi.arch=x86_64, -javaagent:D:\maven-test\org.jmockit_1.20.0.jar, -Dosgi.clean=true,
 -jar, C:\Users\cludwig\.m2\repository\p2\osgi\bundle\org.eclipse.equinox.launcher\1.3.100.v20150511-1540\org.eclipse.equinox.launcher-1.3.100.v20150511-1540.jar, -data, D:\maven-test\test.maventest\t
arget\work\data, -install, D:\maven-test\test.maventest\target\work, -configuration, D:\maven-test\test.maventest\target\work\configuration, -application, org.eclipse.tycho.surefire.osgibooter.headles
stest, -testproperties, D:\maven-test\test.maventest\target\surefire.properties]

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running test.maventest.Foo_UnitTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.118 sec <<< FAILURE! - in test.maventest.Foo_UnitTest
initializationError(test.maventest.Foo_UnitTest)  Time elapsed: 0.002 sec  <<< ERROR!
java.lang.VerifyError: null
        at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
        at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:242)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:236)
        at mockit.internal.mockups.MockClassSetup.applyClassModifications(MockClassSetup.java:134)
        at mockit.internal.mockups.MockClassSetup.redefineMethodsInClassHierarchy(MockClassSetup.java:91)
        at mockit.internal.mockups.MockClassSetup.redefineMethods(MockClassSetup.java:76)
        at mockit.MockUp.redefineMethods(MockUp.java:239)
        at mockit.MockUp.redefineClassOrImplementInterface(MockUp.java:221)
        at mockit.MockUp.<init>(MockUp.java:130)
        at mockit.integration.junit4.internal.RunNotifierDecorator.<init>(RunNotifierDecorator.java:21)
        at mockit.internal.startup.JMockitInitialization.loadInternalStartupMocksForJUnitIntegration(JMockitInitialization.java:35)
        at mockit.internal.startup.JMockitInitialization.initialize(JMockitInitialization.java:26)
        at mockit.internal.startup.Startup.applyStartupMocks(Startup.java:72)
        at mockit.internal.startup.Startup.reapplyStartupMocks(Startup.java:124)
        at mockit.internal.startup.Startup.getInstrumentation(Startup.java:188)
        at mockit.internal.startup.Startup.initializeIfPossible(Startup.java:198)
        at mockit.integration.junit4.JMockit.<clinit>(JMockit.java:21)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
        at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
        at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:262)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:124)
        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:497)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:156)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:82)
        at org.eclipse.tycho.surefire.osgibooter.OsgiSurefireBooter.run(OsgiSurefireBooter.java:91)
        at org.eclipse.tycho.surefire.osgibooter.HeadlessTestApplication.run(HeadlessTestApplication.java:21)
        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:497)
        at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:587)
        at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:198)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)
        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:497)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1515)
        at org.eclipse.equinox.launcher.Main.main(Main.java:1488)


Results :

Tests in error:
  Foo_UnitTest.initializationError ╗ Verify

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] maventest.parent ................................... SUCCESS [  0.219 s]
[INFO] maventest .......................................... SUCCESS [  4.227 s]
[INFO] test.maventest ..................................... FAILURE [  4.320 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19.972 s
[INFO] Finished at: 2015-12-01T08:44:14+01:00
[INFO] Final Memory: 45M/405M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.eclipse.tycho:tycho-surefire-plugin:0.24.0:test (default-test) on project test.maventest: There are test failures.
[ERROR]
[ERROR] Please refer to D:\maven-test\test.maventest\target\surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR]   mvn <goals> -rf :test.maventest

If I remove the -javaagent in the parents POM, then I get another exception:

Running test.maventest.Foo_UnitTest
JMockit: Reinitializing under custom class loader org.eclipse.osgi.internal.loader.EquinoxClassLoader@1cbb87f3[org.jmockit:1.20.0(id=87)]
java.lang.reflect.InvocationTargetException
        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:497)
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
        at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)
Caused by: java.lang.VerifyError
        at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
        at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:242)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:236)
        at mockit.internal.mockups.MockClassSetup.applyClassModifications(MockClassSetup.java:134)
        at mockit.internal.mockups.MockClassSetup.redefineMethodsInClassHierarchy(MockClassSetup.java:91)
        at mockit.internal.mockups.MockClassSetup.redefineMethods(MockClassSetup.java:76)
        at mockit.MockUp.redefineMethods(MockUp.java:239)
        at mockit.MockUp.redefineClassOrImplementInterface(MockUp.java:221)
        at mockit.MockUp.<init>(MockUp.java:130)
        at mockit.integration.junit4.internal.RunNotifierDecorator.<init>(RunNotifierDecorator.java:21)
        at mockit.internal.startup.JMockitInitialization.loadInternalStartupMocksForJUnitIntegration(JMockitInitialization.java:35)
        at mockit.internal.startup.JMockitInitialization.initialize(JMockitInitialization.java:26)
        at mockit.internal.startup.Startup.applyStartupMocks(Startup.java:72)
        at mockit.internal.startup.Startup.reapplyStartupMocks(Startup.java:124)
        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:497)
        at mockit.internal.util.MethodReflection.invoke(MethodReflection.java:94)
        at mockit.internal.util.MethodReflection.invoke(MethodReflection.java:159)
        at mockit.internal.startup.Startup.reinitializeJMockitUnderCustomClassLoader(Startup.java:118)
        at mockit.internal.startup.Startup.agentmain(Startup.java:101)
        ... 6 more
java.lang.IllegalStateException: com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:262)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:124)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:156)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:82)
        at org.eclipse.tycho.surefire.osgibooter.OsgiSurefireBooter.run(OsgiSurefireBooter.java:91)
        at org.eclipse.tycho.surefire.osgibooter.HeadlessTestApplication.run(HeadlessTestApplication.java:21)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:587)
        at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:198)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:669)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:608)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1515)
        at org.eclipse.equinox.launcher.Main.main(Main.java:1488)
Caused by: com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
        ... 26 more
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.382 sec <<< FAILURE! - in test.maventest.Foo_UnitTest
test_value(test.maventest.Foo_UnitTest)  Time elapsed: 0.045 sec  <<< ERROR!
java.lang.IllegalStateException: Invalid place to record expectations
        at test.maventest.Foo_UnitTest$1.<init>(Foo_UnitTest.java:33)
        at test.maventest.Foo_UnitTest.getFoo(Foo_UnitTest.java:33)
        at test.maventest.Foo_UnitTest.test_value(Foo_UnitTest.java:25)


Results :

Tests in error:
  Foo_UnitTest.test_value:25->getFoo:33 ╗ IllegalState Invalid place to record e...

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0

Agent failed to start!
Exception in thread "Attach Listener"

So I conclude that -javaagent is somehow required. Confusing: When using eclipse to run the test it works like a charm!

The jmockit-1.20.0.jar which is passed to -javaagent and therefore is used to run the tests is exactly the same as the one in the eclipse repository. I also added @RunWith(JMockit.class) to the test. As described here, I used all three methods.

So do you have any clue what I can do?


Solution

  • I found a solution: If I execute the tests in Eclipse using Run As -> JUnit Test, all works fine. If I execute the tests using Run As -> JUnit Plugin Test, the same error occurs. Researching gives no result and the support by JMockit is cancelled ("won't fix").

    Since the "normal" tests are working perfectly I'm able to use the "normale" maven-surefire-plugin instead of the tycho-surefire-plugin. The only difference is the test phase (test instead of integration-test) and that there is no OSGi environment (which I do not need). Solution in POM files is as follows.

    parent.pom

    <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>...</groupId>
        <artifactId>...</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>pom</packaging>
    
        <properties>
            <tycho-version>0.24.0</tycho-version>
            <junit-version>4.11</junit-version>
            <maven-surefire-version>2.19</maven-surefire-version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.eclipse.tycho</groupId>
                    <artifactId>tycho-maven-plugin</artifactId>
                    <version>${tycho-version}</version>
                    <extensions>true</extensions>
                </plugin>
    
                <plugin>
                    <groupId>org.eclipse.tycho</groupId>
                    <artifactId>target-platform-configuration</artifactId>
                    <version>${tycho-version}</version>
                    <configuration>
                        <!-- Tell tycho where to find the *.target file. -->
                        <target>
                            <artifact>
                                <groupId>...</groupId>
                                <artifactId>...</artifactId>
                                <version>1.0.0-SNAPSHOT</version>
                            </artifact>
                        </target>
    
                        <environments>
                            <environment>
                                <os>win32</os>
                                <ws>win32</ws>
                                <arch>x86_64</arch>
                            </environment>
                        </environments>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.eclipse.tycho</groupId>
                    <artifactId>tycho-surefire-plugin</artifactId>
                    <version>${tycho-version}</version>
                    <!-- we need to disable the tycho surefire plugin because it
                         runs an integration test. You can simulate it in Eclipse 
                         by executing a "JUnit Plugin test". This is not about to 
                         work with JMockit. Fortunately we do not need OSGi for 
                         the tests so we simply skip this phase. -->
                    <executions>
                        <execution>
                            <id>default-test</id>
                            <phase>none</phase>
                        </execution>
                    </executions>
                </plugin>
    
                <!-- Plugin to update the pom.xml to match the data found in MANIFEST.MF -->
                <plugin>
                    <groupId>org.eclipse.tycho</groupId>
                    <artifactId>tycho-versions-plugin</artifactId>
                    <version>${tycho-version}</version>
                </plugin>
    
                <!-- to run tests in test plugins -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>${maven-surefire-version}</version>
                    <executions>
                        <execution>
                            <id>test</id>
                            <phase>test</phase>
                            <configuration>
                                <testClassesDirectory>target/classes</testClassesDirectory>
                            </configuration>
                            <goals>
                                <goal>test</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit-version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <modules>
            <!-- ... -->
        </modules>
    </project>
    

    All other POM files then have the same structure:

    <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>
        <artifactId>...</artifactId>
        <packaging>eclipse-plugin</packaging>
    
        <parent>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>1.0.0-SNAPSHOT</version>
            <relativePath>...</relativePath>
        </parent>
    </project>
    

    Works perfectly! :)