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

ExceptionInInitializerError inside a JUnit5 unit test (not a @QuarkusTest) since 1.1.0.CR1 #6131

Closed
loicmathieu opened this issue Dec 12, 2019 · 17 comments
Labels
kind/bug Something isn't working
Milestone

Comments

@loicmathieu
Copy link
Contributor

Describe the bug
After migrating to 1.1.0.CR1 from an application running on 1.0.1.Final I have the following error when launching a JUnit5 raw test (a test without using Quakus support via @QuarkusTest).

java.lang.ExceptionInInitializerError
	at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.registerBuiltin(ResteasyProviderFactoryImpl.java:177)
	at org.jboss.resteasy.spi.ResteasyProviderFactory.getInstance(ResteasyProviderFactory.java:127)
	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.getHeaderValueProcessor(AbstractBuiltResponse.java:111)
	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.toHeaderString(AbstractBuiltResponse.java:426)
	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.getHeaderString(AbstractBuiltResponse.java:455)
	at com.adeo.payment.prepaidcard.domain.impl.PrepaidCardServiceImpl.create(PrepaidCardServiceImpl.java:116)
	at com.adeo.payment.prepaidcard.domain.impl.PrepaidCardServiceImplTest.create(PrepaidCardServiceImplTest.java:130)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.lang.IllegalStateException: No configuration is available for this class loader
	at io.smallrye.config.SmallRyeConfigProviderResolver.getConfig(SmallRyeConfigProviderResolver.java:89)
	at org.eclipse.microprofile.config.ConfigProvider.getConfig(ConfigProvider.java:107)
	at org.jboss.resteasy.microprofile.config.ResteasyConfigProvider.getConfig(ResteasyConfigProvider.java:11)
	at org.jboss.resteasy.plugins.providers.RegisterBuiltin$5.run(RegisterBuiltin.java:192)
	at org.jboss.resteasy.plugins.providers.RegisterBuiltin$5.run(RegisterBuiltin.java:189)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at org.jboss.resteasy.plugins.providers.RegisterBuiltin.isGZipEnabled(RegisterBuiltin.java:189)
	at org.jboss.resteasy.plugins.providers.RegisterBuiltin.<clinit>(RegisterBuiltin.java:39)
	... 70 more

Expected behavior
No exception thrown

To Reproduce
I still don't know exactly what happend so it's hard to give a reproducer.

Environment (please complete the following information):

@loicmathieu loicmathieu added the kind/bug Something isn't working label Dec 12, 2019
@mkouba
Copy link
Contributor

mkouba commented Dec 12, 2019

I'm a bit confused. How do you run the test if not using @QuarkusTest?

@loicmathieu
Copy link
Contributor Author

Raw JUnit5 test, I mock all dependencies (for a service: all repositories, for a rest endpoint, all services, ...).

According to me, using @QuarkusTest is for integration test, my definition of a unit test is a test testing only one composant without any dependencies ... So this is a best practice to use raw unit test (without any framework support) and mocking everything so all my tests runs in a few hundreds of ms from my IDE ...

@emmanuelbernard
Copy link
Member

@loicmathieu who starts / calls RESTEasy in your test? Do you start Quarkus yourself somehow?

@loicmathieu
Copy link
Contributor Author

Not starting resteasy nor Quarkus, just plain Java!
I mock everything: my Rest client, my Panache repositories, ...

As I said, it's a unit test, not an integration test, so I did't bootstrap Quarkus and I mock all dependencies of my service.

@gsmet
Copy link
Member

gsmet commented Dec 12, 2019

I think a small reproducer would be welcome. It doesn't have to have a lot of dependencies or whatever, just the gist of it.

@emmanuelbernard
Copy link
Member

Looks like something that did not need mocking, now does. Or at elast your mocking does go as far as org.jboss.resteasy.specimpl.AbstractBuiltResponse.getHeaderString and thus org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl

@mkouba
Copy link
Contributor

mkouba commented Dec 12, 2019

Hm, the PrepaidCardServiceImpl class is probably using javax.ws.rs.core.Response which triggers ResteasyProviderFactory which attempts to access MP Config. So it's very likely a SM Config regression. @dmlloyd @kenfinnigan do you have an idea what's changed?

@loicmathieu
Copy link
Contributor Author

@mkouba I think your analysis of the issue is correct :)
Yes, I use a javax.ws.rs.core.Response and the errors is on response.getHeaderString("Location");

@dmlloyd
Copy link
Member

dmlloyd commented Dec 12, 2019

The issue itself can be fixed by installing a config into QuarkusConfigFactory, and calling ConfigProviderResolver.releaseConfig() on any old config still around, before running your tests. There are some examples in various projects, I'll dig one up after I get back from taking my son to the dentist.

@dmlloyd
Copy link
Member

dmlloyd commented Dec 13, 2019

@loicmathieu
Copy link
Contributor Author

@dmlloyd the issue is fixed by this piece of code

    static volatile SmallRyeConfig config;

    @BeforeAll
    public static void setItUp() {
        final SmallRyeConfigBuilder builder = new SmallRyeConfigBuilder();
        builder.addDefaultSources();
        builder.addDiscoveredConverters();
        builder.addDiscoveredSources();
        config = builder.build();
        QuarkusConfigFactory.setConfig(config);
    }

However I will qualify it of a workaround not a fix. And it works without it in 1.0.1.Final.
Do we really expect all users that does unit test without Quarkus support (and I expect they exist some and maybe a lot of them) to apply this workaround ? How will they find it, should we document it ?

@gsmet gsmet removed the backport? label Dec 13, 2019
@dmlloyd
Copy link
Member

dmlloyd commented Dec 13, 2019

Yes, for now at least. The problem is that the tests assume there is some configuration. But what configuration would it be? Not build time, nor run time, because the test is not running under the purview of Quarkus. Therefore the user must take care to manually set up a configuration; letting the config auto-create encourages leakage (I fixed up a number of these already, which is what led me to the more strict behavior). Leakage impacts testing because the changing the order of test execution can result in different test results if there is more than one configuration in play.

So, manually it can be done via QuarkusConfigFactory as above, or via calls to ConfigProviderResolver to install and remove the configuration, whichever is simpler for the given case.

@loicmathieu
Copy link
Contributor Author

What I would love to see in order to close this issue is the following:

  • Investigate on this precise error (that occurs since 1.1), if it can be fixed good, if not, I'm OK with it as I understand the limitations explained here.
  • Povides a convenient way to create a configuration for Test : QuarkusTestUtils.bootstrapConfiguration() for example.
  • Document the caveats that exist for implementing unit tests without @QuarkusTest as it's very common in an existing code base to have some, and document the way to workaround them (using the new QuarkusTestUtils).

Maybe, as the issue seems to be around http related stuff, we need to provides some http test support to be able to easily mock an HttpRequest, an HttpResponse, HttpHeaders, ...

@machi1990
Copy link
Member

  • Povides a convenient way to create a configuration for Test : QuarkusTestUtils.bootstrapConfiguration() for example.

Hi @loicmathieu @gsmet @dmlloyd we have #5700 which when when fixed will address this point. It will come with documentation as a bonus. I self assigned the issue but as I am on PTO, I have not had the time to look at it implementing it. @loicmathieu or anyone can feel free to give it a go based on the code snippet @dmlloyd shared. Thanks

@sdaschner
Copy link
Contributor

I'm having the same issue and thus not updating to 1.1.1.Final yet. IMO it's crucial to run plain unit tests that can access the regular JAX-RS client without using a config creation workaround and without switching the JAX-RS impl to Jersey or else.

Maybe some tests on running components such as the JAX-RS client (ClientBuilder...) standalone would help mitigating this in the future?

Are there any updates on this? Happy to help in any way, btw.

@sdaschner
Copy link
Contributor

@machi1990 Thanks, using 1.3.0.Alpha1 fixes this for me (running JAX-RS client in plain tests works again 🎉)

@loicmathieu
Copy link
Contributor Author

This fixes also the issue for me, closing it.

@gsmet gsmet added this to the 1.3.0.Alpha1 milestone Feb 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

7 participants