Some time ago, I implemented a remote test execution feature on JUnit 4 for the z2-environment (a Java server development and execution environment for large Java applications). Possibly similar to the Teleporter Rule of Apache Sling (for which I failed to find a JUnit 5 version).
This worked essentially like this:
A custom Runner (Z2UnitTestRunner) is declared on the test class using @RunWith
Z2UnitTestRunner passes a test invocation (actually a test description) to the remote side
On the remote side the test description is executed by a TestExecutor
A registered RunListener logs all test events back to the client side
On the client side any registered RunNotifier will be passed the test events received from the remote side
So it is rather simple actually: It just establishes a man-in-the-middle between Runner and TestExecuter. The cool thing is: While all test execution is performed in the "native" server environment of the application, tests can be triggered from the IDE or ANT/Jenkins, as if running locally. We use that quite a lot.
I am now trying to implement support for JUnit 5. I had a deeper look lately at the various extension and configuration tweaks supported by JUnit 5 but haven't really found a complete solution yet.
The most robust solution, I think, would be to integrate with the DefaultLauncher (as that is used by IDEs and ANT as far as I can tell) or via a custom launcher. The altered behavior would make sure that selectors and filters are sent to the remote side while all TestExecutionListener events would be conveyed to be client side.
Neither approach seems to be supported currently. At least, as far as I can tell, there is no way to provide a custom Launcher nor a way to change the behavior of the DefaultLauncher. But there is a LauncherFactory and a DefaultLauncher - which looks like there IS the intention to support custom launchers (are they?)!
I also looked into implementing a custom engine that would somehow take over test execution delegated to the remote side. But that seems to be the wrong level of interception. Plus I haven't found a way to "suppress" execution via the Jupiter Engine anyway.
Currently I am looking for any good idea or example that would help me move forward. Any suggestion welcome!