> mockConfigurators;
+ private final AtomicBoolean useDefaultConfigProperties = new AtomicBoolean();
+
+ /**
+ *
+ * @param componentClasses The set of components under test
+ */
+ public QuarkusComponentTest(Class>... componentClasses) {
+ this.componentClasses = List.of(componentClasses);
+ this.configProperties = new HashMap<>();
+ this.mockConfigurators = new ArrayList<>();
+ }
+
+ /**
+ * Configure a new mock of a bean.
+ *
+ * Note that a mock is created automatically for all unsatisfied dependencies in the test. This API provides full control
+ * over the bean attributes. The default values are derived from the bean class.
+ *
+ * @param beanClass
+ * @return a new mock bean configurator
+ * @see MockBeanConfigurator#create(Function)
+ */
+ public MockBeanConfigurator mock(Class beanClass) {
+ return new MockBeanConfiguratorImpl<>(this, beanClass);
+ }
+
+ /**
+ * Set a configuration property for the test.
+ *
+ * @param key
+ * @param value
+ * @return the extension
+ */
+ public QuarkusComponentTest configProperty(String key, String value) {
+ this.configProperties.put(key, value);
+ return this;
+ }
+
+ /**
+ * Use the default values for missing config properties. By default, if missing config property results in a test failure.
+ *
+ * For primitives the default values as defined in the JLS are used. For any other type {@code null} is injected.
+ *
+ * @return the extension
+ */
+ public QuarkusComponentTest useDefaultConfigProperties() {
+ this.useDefaultConfigProperties.set(true);
+ return this;
+ }
+
+ @Override
+ public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ // Inject test class fields
+ ArcContainer container = Arc.container();
+ BeanManager beanManager = container.beanManager();
+
+ List> dependentInjectedFields = new ArrayList<>();
+ Class> testClass = context.getRequiredTestClass();
+ for (Field field : testClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(Inject.class)) {
+ InstanceHandle> handle = container.instance(field.getGenericType(), getQualifiers(field, beanManager));
+ field.setAccessible(true);
+ field.set(testInstance, handle.get());
+ if (handle.getBean() != null && handle.getBean().getScope().equals(Dependent.class)) {
+ dependentInjectedFields.add(handle);
+ }
+ }
+ }
+ context.getRoot().getStore(NAMESPACE).put(KEY_DEPENDENT_INJECTED_FIELDS, dependentInjectedFields);
+ LOG.debugf("postProcessTestInstance: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void preDestroyTestInstance(ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ for (InstanceHandle> handle : (List>) context.getRoot().getStore(NAMESPACE)
+ .get(KEY_DEPENDENT_INJECTED_FIELDS, List.class)) {
+ try {
+ handle.destroy();
+ } catch (Exception e) {
+ LOG.errorf(e, "Unable to destroy the injected %s", handle.getBean());
+ }
+ }
+
+ // Unset injected fields
+ Class> testClass = context.getRequiredTestClass();
+ for (Field field : testClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(Inject.class)) {
+ field.setAccessible(true);
+ field.set(context.getRequiredTestInstance(), null);
+ }
+ }
+
+ LOG.debugf("preDestroyTestInstance: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ ClassLoader oldTccl = initArcContainer(context);
+ context.getRoot().getStore(NAMESPACE).put(KEY_OLD_TCCL, oldTccl);
+
+ ConfigProviderResolver oldConfigProviderResolver = ConfigProviderResolver.instance();
+ context.getRoot().getStore(NAMESPACE).put(KEY_OLD_CONFIG_PROVIDER_RESOLVER, oldConfigProviderResolver);
+
+ SmallRyeConfigProviderResolver smallRyeConfigProviderResolver = new SmallRyeConfigProviderResolver();
+ ConfigProviderResolver.setInstance(smallRyeConfigProviderResolver);
+
+ // TCCL is now the QuarkusComponentTestClassLoader set during initialization
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ SmallRyeConfig config = new SmallRyeConfigBuilder().forClassLoader(tccl)
+ //.addDiscoveredConverters()
+ .addDefaultInterceptors()
+ .addDefaultSources()
+ .withSources(this)
+ .build();
+ smallRyeConfigProviderResolver.registerConfig(config, tccl);
+ context.getRoot().getStore(NAMESPACE).put(KEY_CONFIG, config);
+
+ LOG.debugf("beforeAll: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ ClassLoader oldTccl = context.getRoot().getStore(NAMESPACE).get(KEY_OLD_TCCL, ClassLoader.class);
+ Thread.currentThread().setContextClassLoader(oldTccl);
+
+ Arc.shutdown();
+
+ SmallRyeConfig config = context.getRoot().getStore(NAMESPACE).get(KEY_CONFIG, SmallRyeConfig.class);
+ ConfigProviderResolver.instance().releaseConfig(config);
+ ConfigProviderResolver
+ .setInstance(context.getRoot().getStore(NAMESPACE).get(KEY_OLD_CONFIG_PROVIDER_RESOLVER,
+ ConfigProviderResolver.class));
+
+ @SuppressWarnings("unchecked")
+ Set generatedResources = context.getRoot().getStore(NAMESPACE).get(KEY_GENERATED_RESOURCES, Set.class);
+ for (Path path : generatedResources) {
+ try {
+ LOG.debugf("Delete generated %s", path);
+ Files.deleteIfExists(path);
+ } catch (IOException e) {
+ LOG.errorf("Unable to delete the generated resource %s: ", path, e.getMessage());
+ }
+ }
+
+ LOG.debugf("afterAll: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ // Activate the request context
+ ArcContainer container = Arc.container();
+ container.requestContext().activate();
+
+ LOG.debugf("beforeEach: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ long start = System.nanoTime();
+
+ // Terminate the request context
+ ArcContainer container = Arc.container();
+ container.requestContext().terminate();
+
+ LOG.debugf("afterEach: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start));
+ }
+
+ @Override
+ public Set getPropertyNames() {
+ return configProperties.keySet();
+ }
+
+ @Override
+ public String getValue(String propertyName) {
+ return configProperties.get(propertyName);
+ }
+
+ @Override
+ public String getName() {
+ return QuarkusComponentTest.class.getName();
+ }
+
+ void registerMockBean(MockBeanConfiguratorImpl> mock) {
+ this.mockConfigurators.add(mock);
+ }
+
+ private BeanRegistrar registrarForMock(MockBeanConfiguratorImpl> mock) {
+ return new BeanRegistrar() {
+
+ @Override
+ public void register(RegistrationContext context) {
+ BeanConfigurator