Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to inject extensions via Launcher to simplify testing #3747

Open
mauricioaniche opened this issue Mar 27, 2024 · 6 comments
Open

Ability to inject extensions via Launcher to simplify testing #3747

mauricioaniche opened this issue Mar 27, 2024 · 6 comments

Comments

@mauricioaniche
Copy link

I'm writing a pretty complex JUnit plugin and writing lots of unit tests for it. Up to now, my plugin was a test listener. I was then able to test the listener by creating multiple "example unit tests" and running my listener on them, one-by-one, via the Launcher API.

Imagine that, in package fixture1 I have a test suite where all tests are green, and in package fixture2 I have a test suite where some tests are red. I then run these tests with my listener enabled, and I perform assertions.

Below the utility method I use to test the plugin against a specific fixture.

    protected void runTest(DiscoverySelector selector) {

        Launcher launcher = LauncherFactory.create();
        launcher.registerTestExecutionListeners(getExtension());

        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
                .selectors(selector)
                .configurationParameter("junit.jupiter.extensions.autodetection.enabled", "false")
                .build();
        launcher.execute(request);
    }

The getExtension() method allows me to instantiate the listener in the way I want. This is especially important because then I can pass some mocks to its constructor. My plugins make API calls in case specific things happen, and that's why the need for mocks.

Now, I also need the plugin to implement TestExecutionExceptionHandler. However, there's no way for me to configure this handler via the Launcher. I can't use RegisterExtension or autodetection because I have to inject the mock.

Is there any way to inject an extension I'm missing here?

Thanks!

@mpkorstanje
Copy link
Contributor

mpkorstanje commented Mar 27, 2024

I think you can use RegisterExtension. The annotation works on static fields and IIR they do not have to be final, just not null when Jupiter attempts to register the extension. So you can assign to the field prior to running your test.

Though bear in mind that unless you use a singleton, in most circumstances your test listeners and jupiter extension will be different instances.

@marcphilipp
Copy link
Member

There's currently no way to pass an object via the launcher to an engine.

@mauricioaniche Does @mpkorstanje's idea work in your case?

@mauricioaniche
Copy link
Author

Hi, @mpkorstanje, thanks for your comment! The idea of using RegisterExtension doesn't look that elegant to me, because the registration should happen in the test that serves as a fixture for my test, not on the test of the listener itself. I think it'd be easier to understand what I'm saying if I show up some code. I'll try to come up with a small example later!

@marcphilipp I managed to inject the mocked dependencies through some static trickery, so, in this sense, I managed to test what I wanted. I'll also add that to the small example, so you can see it. I was just curious for the reason why we can registerTestExecutionListeners via Launcher, but we can't register extensions.

@mpkorstanje
Copy link
Contributor

I was just curious for the reason why we can registerTestExecutionListeners via Launcher, but we can't register extensions.

It is a consequence of the design of JUnit 5.

The Launcher is part of the JUnit Platform which provides a TestEngine abstraction that is implemented by JUnit Jupiter.

As extensions are a concept specific to the JUnit Jupiter test engine they can't be known by the launcher. Listeners in the other hand are part of the platform.

I think it'd be easier to understand what I'm saying if I show up some code. I'll try to come up with a small example later!

That might help.

By the way, what is the reason for using the TestExecutionExceptionHandler in combination with a test execution listener? They serve rather different purposes.

@mauricioaniche
Copy link
Author

mauricioaniche commented Mar 29, 2024

@mpkorstanje Thanks for the explanation!

I'm not combining them, no! I have implemented a bunch of listeners before and I was always able to test in the way I described above. But I just had to implement a TestExecutionExceptionHandler and when it came to testing, I noticed I couldn't inject mocks to the handler that easily.

Will get back with code soon!

Copy link

If you would like us to be able to process this issue, please provide the requested information. If the information is not provided within the next 3 weeks, we will be unable to proceed and this issue will be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants