Repository: weld/weld-junit (java)

Issue:
MockBean - add convenient static method to create a builder initialized from a bean class
The method would simulate the default bean discovery. And by default, `javax.enterprise.inject.spi.Unmanaged` could be used to create/destroy the bean instance.

Pull request description:
MockBean - add static method to create builder initialized from a class
- resolves #33

Patch:
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/MockBean.java b/junit-common/src/main/java/org/jboss/weld/junit/MockBean.java
index 24a7ac4..89dde42 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/MockBean.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/MockBean.java
@@ -17,22 +17,35 @@
 package org.jboss.weld.junit;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Type;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.enterprise.context.Dependent;
+import javax.enterprise.context.NormalScope;
 import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Alternative;
 import javax.enterprise.inject.Any;
 import javax.enterprise.inject.Default;
+import javax.enterprise.inject.Stereotype;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.InjectionPoint;
 import javax.enterprise.inject.spi.PassivationCapable;
+import javax.enterprise.inject.spi.Unmanaged;
+import javax.enterprise.inject.spi.Unmanaged.UnmanagedInstance;
 import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Named;
+import javax.inject.Qualifier;
+import javax.inject.Scope;
 
 import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+import org.jboss.weld.util.reflection.HierarchyDiscovery;
 
 /**
  * This custom {@link Bean} implementation is useful for mocking.
@@ -83,6 +96,22 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
         return MockBean.<T> builder().types(beanTypes).creating(beanInstance).build();
     }
 
+    /**
+     * A convenient method to create a {@link Builder} initialized from the specified bean class.
+     * <p>
+     * Note that the container may not be started yet and so it is not possible to use CDI SPI. Instead, we try to simulate the default bean discovery.
+     * </p>
+     * <p>
+     * By default, {@link Unmanaged} is used to create/destroy the bean instance. However, it is possible to override this behavior.
+     * </p>
+     *
+     * @param beanClass
+     * @return a new builder instance initialized from the specified bean class
+     */
+    public static <T> Builder<T> read(Class<T> beanClass) {
+        return readInternal(beanClass).useUnmanaged(beanClass);
+    }
+
     private static final AtomicInteger SEQUENCE = new AtomicInteger(0);
 
     private final Set<Class<? extends Annotation>> stereotypes;
@@ -195,6 +224,90 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
         return id;
     }
 
+    private static <T> Builder<T> readInternal(Class<T> beanClass) {
+        // Note that we cannot use BeanManager here as the container may not be started yet
+        Builder<T> builder = new Builder<T>().beanClass(beanClass);
+
+        // Find all stereotypes
+        Set<Annotation> stereotypes = getStereotypes(beanClass);
+        // Name
+        Named named = beanClass.getAnnotation(Named.class);
+        if (named != null) {
+            if ("".equals(named.value())) {
+                builder.name(getDefaultName(beanClass));
+            } else {
+                builder.name(named.value());
+            }
+        } else {
+            for (Annotation stereotype : stereotypes) {
+                if (stereotype.annotationType().isAnnotationPresent(Named.class)) {
+                    builder.name(getDefaultName(beanClass));
+                    break;
+                }
+            }
+        }
+        // Scope
+        Set<Annotation> scopes = getScopes(beanClass);
+        if (scopes.isEmpty()) {
+            for (Annotation stereotype : stereotypes) {
+                scopes.addAll(getScopes(stereotype.annotationType()));
+            }
+        }
+        if (!scopes.isEmpty()) {
+            if (scopes.size() > 1) {
+                throw new IllegalStateException("At most one scope may be specifie [beanClass: " + beanClass + ", scopes: " + scopes + "]");
+            }
+            builder.scope(scopes.iterator().next().annotationType());
+        }
+        // Types
+        builder.types(new HierarchyDiscovery(beanClass).getTypeClosure());
+        // Qualifiers
+        for (Annotation annotation : beanClass.getAnnotations()) {
+            if (annotation.annotationType().isAnnotationPresent(Qualifier.class)) {
+                builder.addQualifier(annotation);
+            }
+        }
+        // Alternative
+        if (beanClass.isAnnotationPresent(Alternative.class)) {
+            builder.alternative(true);
+        } else {
+            for (Annotation stereotype : stereotypes) {
+                if (stereotype.annotationType().isAnnotationPresent(Alternative.class)) {
+                    builder.alternative(true);
+                    break;
+                }
+            }
+        }
+        return builder;
+    }
+
+    private static Set<Annotation> getStereotypes(AnnotatedElement element) {
+        Set<Annotation> stereotypes = new HashSet<>();
+        for (Annotation annotation : element.getAnnotations()) {
+            if (annotation.annotationType().isAnnotationPresent(Stereotype.class)) {
+                stereotypes.add(annotation);
+                stereotypes.addAll(getStereotypes(annotation.annotationType()));
+            }
+        }
+        return stereotypes;
+    }
+
+    private static Set<Annotation> getScopes(AnnotatedElement element) {
+        Set<Annotation> scopes = new HashSet<>();
+        for (Annotation annotation : element.getAnnotations()) {
+            if (annotation.annotationType().isAnnotationPresent(Scope.class) || annotation.annotationType().isAnnotationPresent(NormalScope.class)) {
+                scopes.add(annotation);
+            }
+        }
+        return scopes;
+    }
+
+    private static String getDefaultName(Class<?> beanClass) {
+        StringBuilder defaultName = new StringBuilder(beanClass.getSimpleName());
+        defaultName.setCharAt(0, Character.toLowerCase(beanClass.getSimpleName().charAt(0)));
+        return defaultName.toString();
+    }
+
     /**
      * A builder instance should not be reused nor shared.
      *
@@ -275,11 +388,32 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
          * @see Bean#getTypes()
          */
         public Builder<T> types(Type... types) {
-            this.types = new HashSet<>();
+            this.types.clear();
             Collections.addAll(this.types, types);
             return this;
         }
 
+        /**
+         *
+         * @param types
+         * @return self
+         */
+        public Builder<T> types(Set<Type> types) {
+            this.types.clear();
+            this.types.addAll(types);
+            return this;
+        }
+
+        /**
+         *
+         * @param type
+         * @return self
+         */
+        public Builder<T> addType(Type type) {
+            this.types.add(type);
+            return this;
+        }
+
         /**
          *
          * @param qualifiers
@@ -287,11 +421,21 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
          * @see Bean#getQualifiers()
          */
         public Builder<T> qualifiers(Annotation... qualifiers) {
-            this.qualifiers = new HashSet<>();
+            this.qualifiers.clear();
             Collections.addAll(this.qualifiers, qualifiers);
             return this;
         }
 
+        /**
+         *
+         * @param qualifier
+         * @return self
+         */
+        public Builder<T> addQualifier(Annotation qualifier) {
+            this.qualifiers.add(qualifier);
+            return this;
+        }
+
         /**
          *
          * @param value
@@ -345,11 +489,21 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
          */
         @SuppressWarnings("unchecked")
         public Builder<T> stereotypes(Class<? extends Annotation>... stereotypes) {
-            this.stereotypes = new HashSet<>();
+            this.stereotypes.clear();
             Collections.addAll(this.stereotypes, stereotypes);
             return this;
         }
 
+        /**
+         *
+         * @param qualifier
+         * @return self
+         */
+        public Builder<T> addStereotype(Class<? extends Annotation> stereotype) {
+            this.stereotypes.add(stereotype);
+            return this;
+        }
+
         /**
          * Each invocation of {@link Bean#create(CreationalContext)} will return the same instance.
          *
@@ -357,12 +511,40 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
          * @return self
          */
         public Builder<T> creating(final T instance) {
-            this.createCallback = new CreateFunction<T>() {
-                @Override
-                public T create(CreationalContext<T> creationalContext) {
-                    return instance;
+            this.createCallback = ctx -> instance;
+            return this;
+        }
+
+        /**
+         * Use {@link Unmanaged} to create/destroy the bean instance.
+         *
+         * <p>
+         * NOTE: {@link CreationalContext#toString()} is used as a key in a map and therefore must be unique for the lifetime of a bean instance. Weld
+         * implementation fulfills this requirement.
+         * </p>
+         *
+         * @return self
+         * @see UnmanagedInstance
+         */
+        @SuppressWarnings("unchecked")
+        public Builder<T> useUnmanaged(Class<T> beanClass) {
+            Map<String, UnmanagedInstance<?>> ctxToUnmanaged = new ConcurrentHashMap<>();
+            create(ctx -> {
+                Unmanaged<?> unmanaged = new Unmanaged<>(WeldContainer.current().getBeanManager(), beanClass);
+                UnmanagedInstance<?> unmanagedInstance = unmanaged.newInstance();
+                ctxToUnmanaged.put(ctx.toString(), unmanagedInstance);
+                return (T) unmanagedInstance.produce().inject().postConstruct().get();
+            });
+            destroy((o, ctx) -> {
+                UnmanagedInstance<?> unmanagedInstance = ctxToUnmanaged.remove(ctx.toString());
+                if (unmanagedInstance != null) {
+                    if (!unmanagedInstance.get().equals(o)) {
+                        throw new IllegalStateException(
+                                "Unmanaged instance [" + unmanagedInstance.get() + "] is not equal to the bean instance to be destroyed: " + o);
+                    }
+                    unmanagedInstance.preDestroy().dispose();
                 }
-            };
+            });
             return this;
         }
 
@@ -398,7 +580,19 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
                 throw new IllegalStateException("Create callback must not be null");
             }
             if (qualifiers.size() == 1) {
-                qualifiers.add(DefaultLiteral.INSTANCE);
+                Annotation qualifier = qualifiers.iterator().next();
+                if (qualifier.annotationType().equals(Named.class) || qualifier.equals(AnyLiteral.INSTANCE)) {
+                    // Single qualifier - @Named or @Any
+                    qualifiers.add(DefaultLiteral.INSTANCE);
+                }
+            } else if (qualifiers.size() == 2 && qualifiers.contains(AnyLiteral.INSTANCE)) {
+                for (Annotation qualifier : qualifiers) {
+                    if (qualifier.annotationType().equals(Named.class)) {
+                        // Two qualifiers - @Named and @Any
+                        qualifiers.add(DefaultLiteral.INSTANCE);
+                        break;
+                    }
+                }
             }
             return new MockBean<>(beanClass, stereotypes, alternative, selectForSyntheticBeanArchive, name, qualifiers, types, scope, createCallback,
                     destroyCallback);
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
index 549bdaf..0f7212e 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
@@ -25,6 +25,7 @@ import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.enterprise.inject.spi.BeanManager;
 
@@ -93,32 +94,42 @@ public class WeldJunit5Extension implements AfterAllCallback, TestInstancePostPr
         // store info about explicit param injection, either from global settings or from annotation on the test class
         storeExplicitParamResolutionInformation(context);
 
+        // all found fields which are WeldInitiator and have @WeldSetup annotation
+        List<Field> foundInitiatorFields = new ArrayList<>();
         WeldInitiator initiator = null;
-        // First try to find @WeldSetup field
-        for (Field field : testInstance.getClass().getDeclaredFields()) {
-            if (field.isAnnotationPresent(WeldSetup.class)) {
-                if (initiator != null) {
-                    // multiple fields found, throw exception
-                    throw new IllegalStateException("Multiple @WeldSetup annotated fields found, please use only one such field.");
-                }
-                Object fieldInstance;
-                try {
-                    fieldInstance = field.get(testInstance);
-                } catch (IllegalAccessException e) {
-                    // In case we cannot get to the field, we need to set accessibility as well
-                    field.setAccessible(true);
-                    fieldInstance = field.get(testInstance);
-                }
-                // if it's null, we can still store it, it will be created with default settings later on
-                if (fieldInstance != null && fieldInstance instanceof WeldInitiator) {
-                    initiator = (WeldInitiator) fieldInstance;
-                } else {
-                    // Field with other type than WeldInitiator was annotated with @WeldSetup
-                    throw new IllegalStateException("@WeldSetup annotation should only be used on a field of type WeldInitiator.");
+        // We will go through class hierarchy in search of @WeldSetup field (even private)
+        for (Class<?> clazz = testInstance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
+            // Find @WeldSetup field using getDeclaredFields() - this allows even for private fields
+            for (Field field : clazz.getDeclaredFields()) {
+                if (field.isAnnotationPresent(WeldSetup.class)) {
+                    Object fieldInstance;
+                    try {
+                        fieldInstance = field.get(testInstance);
+                    } catch (IllegalAccessException e) {
+                        // In case we cannot get to the field, we need to set accessibility as well
+                        field.setAccessible(true);
+                        fieldInstance = field.get(testInstance);
+                    }
+                    if (fieldInstance != null && fieldInstance instanceof WeldInitiator) {
+                        initiator = (WeldInitiator) fieldInstance;
+                        foundInitiatorFields.add(field);
+                    } else {
+                        // Field with other type than WeldInitiator was annotated with @WeldSetup
+                        throw new IllegalStateException("@WeldSetup annotation should only be used on a field of type"
+                            + " WeldInitiator but was found on a field of type " + field.getType() + " which is declared "
+                            + "in class " + field.getDeclaringClass());
+                    }
                 }
             }
         }
-        // WeldInitiator may still be null if user didn't specify it at all, we need to create it
+        // Multiple occurrences of @WeldSetup in the hierarchy will lead to an exception 
+        if (foundInitiatorFields.size() > 1) {
+            throw new IllegalStateException(foundInitiatorFields.stream().map(f -> "Field type - " + f.getType() + " which is "
+                + "in " + f.getDeclaringClass()).collect(Collectors.joining("\n", "Multiple @WeldSetup annotated fields found, "
+                        + "only one is allowed! Fields found:\n", "")));
+        }
+
+        // at this point we can be sure that either no or exactly one WeldInitiator was found
         if (initiator == null) {
             initiator = WeldInitiator.from(AbstractWeldInitiator.createWeld().addPackage(false, testInstance.getClass())).build();
         }


Test patch:
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/bean/AddBeanTest.java b/junit4/src/test/java/org/jboss/weld/junit4/bean/AddBeanTest.java
index b257e2b..62b0a95 100644
--- a/junit4/src/test/java/org/jboss/weld/junit4/bean/AddBeanTest.java
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/AddBeanTest.java
@@ -17,22 +17,24 @@
 package org.jboss.weld.junit4.bean;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.context.Dependent;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.util.TypeLiteral;
 
 import org.jboss.weld.junit.MockBean;
-import org.jboss.weld.junit.MockBean.CreateFunction;
 import org.jboss.weld.junit4.WeldInitiator;
 import org.junit.Rule;
 import org.junit.Test;
@@ -48,8 +50,9 @@ public class AddBeanTest {
 
     @Rule
     public WeldInitiator weld = WeldInitiator.from(Blue.class)
-            .addBeans(MockBean.of(mock(MyService.class), MyService.class), createListBean(),
-                    createSequenceBean(), createIdSupplierBean())
+            .addBeans(MockBean.of(mock(MyService.class), MyService.class))
+            .addBeans(MockBean.read(BlueToDiscover.class).scope(Dependent.class).build())
+            .addBeans(createListBean(), createSequenceBean(), createIdSupplierBean())
             .build();
 
     @SuppressWarnings("serial")
@@ -67,14 +70,7 @@ public class AddBeanTest {
         return MockBean.<Integer> builder()
                 .types(Integer.class)
                 .qualifiers(Meaty.Literal.INSTANCE)
-                .create(
-                        // We could use lambda in Java8
-                        new CreateFunction<Integer>() {
-                            @Override
-                            public Integer create(CreationalContext<Integer> creationalContext) {
-                                return SEQUENCE.incrementAndGet();
-                            }
-                        })
+                .create(ctx -> SEQUENCE.incrementAndGet())
                 .build();
     }
 
@@ -82,12 +78,7 @@ public class AddBeanTest {
         return MockBean.<IdSupplier> builder()
                 .types(IdSupplier.class)
                 .scope(ApplicationScoped.class)
-                .create(new CreateFunction<IdSupplier>() {
-                    @Override
-                    public IdSupplier create(CreationalContext<IdSupplier> creationalContext) {
-                        return new IdSupplier(UUID.randomUUID().toString());
-                    }
-                }).build();
+                .create(ctx -> new IdSupplier(UUID.randomUUID().toString())).build();
     }
 
     @Test
@@ -109,6 +100,15 @@ public class AddBeanTest {
 
         // Test applicaction scoped bean
         assertEquals(weld.select(IdSupplier.class).get().getId(), weld.select(IdSupplier.class).get().getId());
+
+        // The scope is changed to @Dependent
+        BlueToDiscover blue1 = weld.select(BlueToDiscover.class).get();
+        BlueToDiscover blue2 = weld.select(BlueToDiscover.class).get();
+        assertNotNull(blue1.getId());
+        assertNotNull(blue2.getId());
+        assertNotEquals(blue1.getId(), blue2.getId());
+        Set<Bean<?>> beans = weld.getBeanManager().getBeans("blue");
+        assertEquals(1, beans.size());
     }
 
     interface MyService {
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/bean/BlueToDiscover.java b/junit4/src/test/java/org/jboss/weld/junit4/bean/BlueToDiscover.java
new file mode 100644
index 0000000..0072f6e
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/BlueToDiscover.java
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit4.bean;
+
+import java.util.UUID;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+@Named("blue")
+@ApplicationScoped
+public class BlueToDiscover {
+
+    private String id;
+
+    @PostConstruct
+    void init() {
+        this.id = UUID.randomUUID().toString();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/bean/AddBeanTest.java b/junit5/src/test/java/org/jboss/weld/junit5/bean/AddBeanTest.java
index 28a7ca9..33d222b 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/bean/AddBeanTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/bean/AddBeanTest.java
@@ -16,21 +16,25 @@
  */
 package org.jboss.weld.junit5.bean;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.context.Dependent;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.util.TypeLiteral;
 
 import org.jboss.weld.junit.MockBean;
-import org.jboss.weld.junit.MockBean.CreateFunction;
 import org.jboss.weld.junit5.WeldInitiator;
 import org.jboss.weld.junit5.WeldJunit5Extension;
 import org.jboss.weld.junit5.WeldSetup;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.ArgumentMatchers;
@@ -47,9 +51,10 @@ public class AddBeanTest {
 
     @WeldSetup
     public WeldInitiator weld = WeldInitiator.from(Blue.class)
-        .addBeans(MockBean.of(Mockito.mock(MyService.class), MyService.class), createListBean(),
-            createSequenceBean(), createIdSupplierBean())
-        .build();
+            .addBeans(MockBean.of(mock(MyService.class), MyService.class))
+            .addBeans(MockBean.read(BlueToDiscover.class).scope(Dependent.class).build())
+            .addBeans(createListBean(), createSequenceBean(), createIdSupplierBean())
+            .build();
 
     @SuppressWarnings("serial")
     static Bean<?> createListBean() {
@@ -68,37 +73,24 @@ public class AddBeanTest {
             .types(Integer.class)
             .qualifiers(Meaty.Literal.INSTANCE)
             .create((ctx) -> SEQUENCE.incrementAndGet()).build();
-//                        // We could use lambda in Java8
-//                        new CreateFunction<Integer>() {
-//                            @Override
-//                            public Integer create(CreationalContext<Integer> creationalContext) {
-//                                return SEQUENCE.incrementAndGet();
-//                            }
-//                        })
-//                .build();
     }
 
     static Bean<?> createIdSupplierBean() {
         return MockBean.<IdSupplier>builder()
             .types(IdSupplier.class)
             .scope(ApplicationScoped.class)
-            .create(new CreateFunction<IdSupplier>() {
-                @Override
-                public IdSupplier create(CreationalContext<IdSupplier> creationalContext) {
-                    return new IdSupplier(UUID.randomUUID().toString());
-                }
-            }).build();
+            .create(ctx -> new IdSupplier(UUID.randomUUID().toString())).build();
     }
 
     @Test
     public void testBeansAdded() {
         // Blue injects @Meaty List<String>
-        Assertions.assertEquals("42", weld.select(Blue.class).get().getStringList().get(0));
+        assertEquals("42", weld.select(Blue.class).get().getStringList().get(0));
 
         // Each Bean.create() increments the sequence
         SEQUENCE.set(0);
         for (int i = 1; i < 11; i++) {
-            Assertions.assertEquals(Integer.valueOf(i),
+            assertEquals(Integer.valueOf(i),
                 weld.select(Integer.class, Meaty.Literal.INSTANCE).get());
         }
 
@@ -108,7 +100,16 @@ public class AddBeanTest {
         Mockito.verify(myService, Mockito.atLeastOnce()).doBusiness(ArgumentMatchers.anyString());
 
         // Test applicaction scoped bean
-        Assertions.assertEquals(weld.select(IdSupplier.class).get().getId(), weld.select(IdSupplier.class).get().getId());
+        assertEquals(weld.select(IdSupplier.class).get().getId(), weld.select(IdSupplier.class).get().getId());
+
+        // The scope is changed to @Dependent
+        BlueToDiscover blue1 = weld.select(BlueToDiscover.class).get();
+        BlueToDiscover blue2 = weld.select(BlueToDiscover.class).get();
+        assertNotNull(blue1.getId());
+        assertNotNull(blue2.getId());
+        assertNotEquals(blue1.getId(), blue2.getId());
+        Set<Bean<?>> beans = weld.getBeanManager().getBeans("blue");
+        assertEquals(1, beans.size());
     }
 
     interface MyService {
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/bean/BlueToDiscover.java b/junit5/src/test/java/org/jboss/weld/junit5/bean/BlueToDiscover.java
new file mode 100644
index 0000000..811ab43
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/bean/BlueToDiscover.java
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.bean;
+
+import java.util.UUID;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Named;
+
+@Named("blue")
+@ApplicationScoped
+public class BlueToDiscover {
+
+    private String id;
+
+    @PostConstruct
+    void init() {
+        this.id = UUID.randomUUID().toString();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/bean/Foo.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/bean/Foo.java
new file mode 100644
index 0000000..1641bda
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/bean/Foo.java
@@ -0,0 +1,6 @@
+package org.jboss.weld.junit5.initiator.bean;
+
+public class Foo
+{
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorInSuperclassTest.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorInSuperclassTest.java
new file mode 100644
index 0000000..b44dca9
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorInSuperclassTest.java
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.initiator.discovery;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.jboss.weld.environment.se.WeldContainer;
+import org.jboss.weld.junit5.EnableWeld;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests a case where WeldInitiator resides in a private field within a superclass
+ *
+ * @author <a href="mailto:manovotn@redhat.com">Matej Novotny</a>
+ */
+@EnableWeld
+public class PrivateWeldInitiatorInSuperclassTest extends SuperclassWithPrivateWeldInitiator {
+
+    @Test
+    public void testSuperclassPrivateWeldInit() {
+        final Foo foo = WeldContainer.current().select(Foo.class).get();
+        assertNotNull(foo);
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorTest.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorTest.java
new file mode 100644
index 0000000..bb53466
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/PrivateWeldInitiatorTest.java
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.initiator.discovery;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.junit5.EnableWeld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldSetup;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests a case where WeldInitiator is a private field in a test class
+ *
+ * @author <a href="mailto:manovotn@redhat.com">Matej Novotny</a>
+ */
+@EnableWeld
+public class PrivateWeldInitiatorTest {
+
+    @WeldSetup
+    private WeldInitiator weld = WeldInitiator
+        .of(new Weld()
+            .addBeanClass(Foo.class));
+
+    @Test
+    public void testPrivateWeldInit() {
+        final Foo foo = weld.select(Foo.class).get();
+        assertNotNull(foo);
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/ProtectedWeldInitiatorInSuperclassTest.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/ProtectedWeldInitiatorInSuperclassTest.java
new file mode 100644
index 0000000..5d9af3c
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/ProtectedWeldInitiatorInSuperclassTest.java
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.initiator.discovery;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.jboss.weld.junit5.EnableWeld;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests a case where WeldInitiator comes from a protected field in a superclass
+ *
+ * @author <a href="mailto:manovotn@redhat.com">Matej Novotny</a>
+ */
+@EnableWeld
+public class ProtectedWeldInitiatorInSuperclassTest extends SuperclassWithProtectedWeldInitiator {
+
+    @Test
+    public void testSuperclassProtectedWeldInit() {
+        final Foo foo = weld.select(Foo.class).get();
+        assertNotNull(foo);
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SubclassWithWeldInitiatorTest.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SubclassWithWeldInitiatorTest.java
new file mode 100644
index 0000000..c25cc13
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SubclassWithWeldInitiatorTest.java
@@ -0,0 +1,16 @@
+package org.jboss.weld.junit5.initiator.discovery;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.jboss.weld.junit5.initiator.bean.Foo;
+import org.junit.jupiter.api.Test;
+
+public class SubclassWithWeldInitiatorTest extends SuperclassWithWeldInitiator
+{
+	@Test
+	public void testSuperclassWeldInit()
+	{
+		final Foo foo = weld.select(Foo.class).get();
+		assertNotNull(foo);
+	}
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithPrivateWeldInitiator.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithPrivateWeldInitiator.java
new file mode 100644
index 0000000..dd355c2
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithPrivateWeldInitiator.java
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.initiator.discovery;
+
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldSetup;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+
+/**
+ *
+ * @author <a href="mailto:manovotn@redhat.com">Matej Novotny</a>
+ */
+public class SuperclassWithPrivateWeldInitiator {
+
+    @WeldSetup
+    private WeldInitiator weld = WeldInitiator
+        .of(new Weld()
+            .addBeanClass(Foo.class));
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithProtectedWeldInitiator.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithProtectedWeldInitiator.java
new file mode 100644
index 0000000..17c411e
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithProtectedWeldInitiator.java
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2018, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.junit5.initiator.discovery;
+
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldSetup;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+
+/**
+ *
+ * @author <a href="mailto:manovotn@redhat.com">Matej Novotny</a>
+ */
+public class SuperclassWithProtectedWeldInitiator {
+
+    @WeldSetup
+    protected WeldInitiator weld = WeldInitiator
+        .of(new Weld()
+            .addBeanClass(Foo.class));
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithWeldInitiator.java b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithWeldInitiator.java
new file mode 100644
index 0000000..e85b12a
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/initiator/discovery/SuperclassWithWeldInitiator.java
@@ -0,0 +1,17 @@
+package org.jboss.weld.junit5.initiator.discovery;
+
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldJunit5Extension;
+import org.jboss.weld.junit5.WeldSetup;
+import org.jboss.weld.junit5.initiator.bean.Foo;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(WeldJunit5Extension.class)
+public abstract class SuperclassWithWeldInitiator
+{
+	@WeldSetup
+	public WeldInitiator weld = WeldInitiator
+			.of(new Weld()
+					.addBeanClass(Foo.class));
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/resources/InjectResourcesTest.java b/junit5/src/test/java/org/jboss/weld/junit5/resources/InjectResourcesTest.java
index 8eae12c..fe6635c 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/resources/InjectResourcesTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/resources/InjectResourcesTest.java
@@ -57,7 +57,7 @@ import org.junit.jupiter.api.Test;
 public class InjectResourcesTest {
 
     @WeldSetup
-    WeldInitiator weld = WeldInitiator.fromTestPackage()
+    public WeldInitiator weld = WeldInitiator.fromTestPackage()
                                                 .bindResource("bar", "hello1")
                                                 .bindResource("java:comp/env/baz", "hello2")
                                                 .setEjbFactory(ip -> new DummySessionBean("ping"))