Repository: weld/weld-junit (java)

Issue:
Make it possible to add the specified beans during AfterBeanDiscovery notification
* add `WeldInitiator.Builder.addBeans(Bean<?>...)`
* provide a simple mock bean builder

Pull request:
WeldInitiator - make it possible to add beans during AfterBeanDiscovery
- also add MockBean with builder
- resolves #4

Patch:
diff --git a/junit4/pom.xml b/junit4/pom.xml
index d2cc3d3..a0f4c9c 100644
--- a/junit4/pom.xml
+++ b/junit4/pom.xml
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <parent>
@@ -21,6 +22,12 @@
          <artifactId>junit</artifactId>
       </dependency>
 
+      <!-- Test dependencies -->
+      <dependency>
+         <groupId>org.mockito</groupId>
+         <artifactId>mockito-core</artifactId>
+      </dependency>
+
    </dependencies>
 
    <build>
diff --git a/junit4/src/main/java/org/jboss/weld/junit4/MockBean.java b/junit4/src/main/java/org/jboss/weld/junit4/MockBean.java
new file mode 100644
index 0000000..b1215d4
--- /dev/null
+++ b/junit4/src/main/java/org/jboss/weld/junit4/MockBean.java
@@ -0,0 +1,373 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2017, 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;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Default;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.InjectionPoint;
+import javax.enterprise.util.AnnotationLiteral;
+
+/**
+ * This custom {@link Bean} implementation is useful for mocking.
+ * <p>
+ * A new instance is usually created through a {@link Builder} (see also {@link #builder()}) and then passed to the
+ * {@link WeldInitiator.Builder#addBeans(Bean...)} method.
+ * </p>
+ *
+ * @author Martin Kouba
+ *
+ * @param <T>
+ * @see WeldInitiator.Builder#addBean(Bean)
+ */
+public class MockBean<T> implements Bean<T> {
+
+    /**
+     * By default, the bean:
+     * <ul>
+     * <li>has no name</li>
+     * <li>has {@link Dependent} scope</li>
+     * <li>has {@link Any} qualifier and {@link Default} is added automatically if no other qualifiers are set</li>
+     * <li>has {@link Object} bean type</li>
+     * <li>has no stereotypes</li>
+     * <li>is not an alternative</li>
+     * </ul>
+     *
+     * <p>
+     * Note that {@link Builder#creating(Object)} or {@link Builder#create(CreateFunction)} must be always set. Otherwise, an {@link IllegalStateException} is
+     * thrown during {@link Builder#build()} invocation.
+     * <p>
+     *
+     * @return a new builder instance
+     */
+    public static <T> Builder<T> builder() {
+        return new Builder<>();
+    }
+
+    /**
+     * A convenient method to create a {@link Bean} with default values (see also {@link #builder()}). Additionaly, the specified bean type is added to the set
+     * of bean types and {@link Bean#create(CreationalContext)} will always return the specified bean instance.
+     *
+     * @param beanType
+     * @param beanInstance
+     * @return a {@link MockBean} instance
+     */
+    public static <T> Bean<T> of(Type beanType, T beanInstance) {
+        return MockBean.<T> builder().types(beanType).creating(beanInstance).build();
+    }
+
+    private final Set<Class<? extends Annotation>> stereotypes;
+
+    private final boolean alternative;
+
+    private final String name;
+
+    private final Set<Annotation> qualifiers;
+
+    private final Set<Type> types;
+
+    private final Class<? extends Annotation> scope;
+
+    private final CreateFunction<T> createCallback;
+
+    private final DestroyFunction<T> destroyCallback;
+
+    private MockBean(Set<Class<? extends Annotation>> stereotypes, boolean alternative, String name,
+            Set<Annotation> qualifiers, Set<Type> types, Class<? extends Annotation> scope,
+            CreateFunction<T> createCallback, DestroyFunction<T> destroyCallback) {
+        this.stereotypes = stereotypes;
+        this.alternative = alternative;
+        this.name = name;
+        this.qualifiers = qualifiers;
+        this.types = types;
+        this.scope = scope;
+        this.createCallback = createCallback;
+        this.destroyCallback = destroyCallback;
+    }
+
+    @Override
+    public T create(CreationalContext<T> creationalContext) {
+        return createCallback.create(creationalContext);
+    }
+
+    @Override
+    public void destroy(T instance, CreationalContext<T> creationalContext) {
+        if (destroyCallback != null) {
+            destroyCallback.destroy(instance, creationalContext);
+        }
+    }
+
+    @Override
+    public Class<?> getBeanClass() {
+        return WeldJunit4Extension.class;
+    }
+
+    @Override
+    public Set<InjectionPoint> getInjectionPoints() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public boolean isNullable() {
+        return false;
+    }
+
+    @Override
+    public Set<Type> getTypes() {
+        return types;
+    }
+
+    @Override
+    public Set<Annotation> getQualifiers() {
+        return qualifiers;
+    }
+
+    @Override
+    public Class<? extends Annotation> getScope() {
+        return scope;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Set<Class<? extends Annotation>> getStereotypes() {
+        return stereotypes;
+    }
+
+    @Override
+    public boolean isAlternative() {
+        return alternative;
+    }
+
+    /**
+     * A builder instance should not be reused nor shared.
+     *
+     * @author Martin Kouba
+     *
+     * @param <T>
+     */
+    public static class Builder<T> {
+
+        private Set<Class<? extends Annotation>> stereotypes;
+
+        private boolean alternative;
+
+        private String name;
+
+        private Set<Annotation> qualifiers;
+
+        private Set<Type> types;
+
+        private Class<? extends Annotation> scope;
+
+        private CreateFunction<T> createCallback;
+
+        private DestroyFunction<T> destroyCallback;
+
+        Builder() {
+            this.stereotypes = new HashSet<>();
+            this.alternative = false;
+            this.qualifiers = new HashSet<>();
+            this.qualifiers.add(AnyLiteral.INSTANCE);
+            this.scope = Dependent.class;
+            this.types = new HashSet<>();
+            this.types.add(Object.class);
+        }
+
+        /**
+         *
+         * @param scope
+         * @return self
+         * @see Bean#getScope()
+         */
+        public Builder<T> scope(Class<? extends Annotation> scope) {
+            this.scope = scope;
+            return this;
+        }
+
+        /**
+         *
+         * @param name
+         * @return self
+         * @see Bean#getName()
+         */
+        public Builder<T> name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         *
+         * @param types
+         * @return self
+         * @see Bean#getTypes()
+         */
+        public Builder<T> types(Type... types) {
+            this.types = new HashSet<>();
+            Collections.addAll(this.types, types);
+            return this;
+        }
+
+        /**
+         *
+         * @param qualifiers
+         * @return self
+         * @see Bean#getQualifiers()
+         */
+        public Builder<T> qualifiers(Annotation... qualifiers) {
+            this.qualifiers = new HashSet<>();
+            Collections.addAll(this.qualifiers, qualifiers);
+            return this;
+        }
+
+        /**
+         *
+         * @param value
+         * @return self
+         * @see Bean#isAlternative()
+         */
+        public Builder<T> alternative(boolean value) {
+            this.alternative = value;
+            return this;
+        }
+
+        /**
+         *
+         * @param stereotypes
+         * @return self
+         * @see Bean#getStereotypes()
+         */
+        @SuppressWarnings("unchecked")
+        public Builder<T> stereotypes(Class<? extends Annotation>... stereotypes) {
+            this.stereotypes = new HashSet<>();
+            Collections.addAll(this.stereotypes, stereotypes);
+            return this;
+        }
+
+        /**
+         * Each invocation of {@link Bean#create(CreationalContext)} will return the same instance.
+         *
+         * @param instance
+         * @return self
+         */
+        public Builder<T> creating(final T instance) {
+            this.createCallback = new CreateFunction<T>() {
+                @Override
+                public T create(CreationalContext<T> creationalContext) {
+                    return instance;
+                }
+            };
+            return this;
+        }
+
+        /**
+         *
+         * @param callback
+         * @return self
+         * @see Bean#create(CreationalContext)
+         */
+        public Builder<T> create(CreateFunction<T> callback) {
+            this.createCallback = callback;
+            return this;
+        }
+
+        /**
+         *
+         * @param callback
+         * @return self
+         * @see Bean#destroy(Object, CreationalContext)
+         */
+        public Builder<T> destroy(DestroyFunction<T> callback) {
+            this.destroyCallback = callback;
+            return this;
+        }
+
+        /**
+         *
+         * @return a new {@link MockBean} instance
+         * @throws IllegalStateException If a create callback is not set
+         */
+        public MockBean<T> build() {
+            if (createCallback == null) {
+                throw new IllegalStateException("Create callback must not be null");
+            }
+            if (qualifiers.size() == 1) {
+                qualifiers.add(DefaultLiteral.INSTANCE);
+            }
+            return new MockBean<>(stereotypes, alternative, name, qualifiers, types, scope,
+                    createCallback, destroyCallback);
+        }
+
+    }
+
+    public interface CreateFunction<T> {
+
+        /**
+         *
+         * @param creationalContext
+         * @return a new bean instance
+         */
+        T create(CreationalContext<T> creationalContext);
+
+    }
+
+    public interface DestroyFunction<T> {
+
+        /**
+         *
+         * @param instance
+         * @param creationalContext
+         * @return a new bean instance
+         */
+        void destroy(T instance, CreationalContext<T> creationalContext);
+
+    }
+
+    @SuppressWarnings("all")
+    static class AnyLiteral extends AnnotationLiteral<Any> implements Any {
+
+        private static final long serialVersionUID = 1L;
+
+        public static final Any INSTANCE = new AnyLiteral();
+
+        private AnyLiteral() {
+        }
+
+    }
+
+    @SuppressWarnings("all")
+    static class DefaultLiteral extends AnnotationLiteral<Default> implements Default {
+
+        public static final Default INSTANCE = new DefaultLiteral();
+
+        private DefaultLiteral() {
+        }
+
+    }
+
+}
diff --git a/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java b/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
index a2ef321..3ce77a0 100644
--- a/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
+++ b/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
@@ -18,6 +18,7 @@ package org.jboss.weld.junit4;
 
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -28,6 +29,8 @@ import javax.enterprise.context.NormalScope;
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.event.Event;
 import javax.enterprise.inject.Instance;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.InjectionTarget;
 import javax.enterprise.util.TypeLiteral;
@@ -85,7 +88,7 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
      * @return a new test rule
      */
     public static WeldInitiator of(Weld weld) {
-        return new WeldInitiator(weld, null, null);
+        return new WeldInitiator(weld, null, null, null);
     }
 
     /**
@@ -94,7 +97,7 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
      * @return a new test rule
      */
     public static WeldInitiator ofTestPackage() {
-        return new WeldInitiator(null, null, null);
+        return new WeldInitiator(null, null, null, null);
     }
 
     /**
@@ -154,10 +157,13 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
 
         private final Set<Class<? extends Annotation>> scopesToActivate;
 
+        private final Set<Bean<?>> beans;
+
         public Builder(Weld weld) {
             this.weld = weld;
             this.instancesToInject = new ArrayList<>();
             this.scopesToActivate = new HashSet<>();
+            this.beans = new HashSet<>();
         }
 
         /**
@@ -213,13 +219,27 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
             return this;
         }
 
+        /**
+         * Instructs the {@link WeldInitiator} to add the specified beans during {@link AfterBeanDiscovery} notification.
+         *
+         * @param beans
+         * @return self
+         * @see AfterBeanDiscovery#addBean(Bean)
+         */
+        public Builder addBeans(Bean<?>... beans) {
+            Collections.addAll(this.beans, beans);
+            return this;
+        }
+
         /**
          *
          * @return a new {@link WeldInitiator} instance
          */
         public WeldInitiator build() {
-            return new WeldInitiator(weld, new ArrayList<>(instancesToInject),
-                    new HashSet<>(scopesToActivate));
+            return new WeldInitiator(weld,
+                    instancesToInject.isEmpty() ? null : new ArrayList<>(instancesToInject),
+                    scopesToActivate.isEmpty() ? null : new HashSet<>(scopesToActivate),
+                    beans.isEmpty() ? null : new HashSet<>(beans));
         }
 
     }
@@ -230,17 +250,20 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
 
     private final Set<Class<? extends Annotation>> scopesToActivate;
 
+    private final Set<Bean<?>> beans;
+
     private final WeldJunit4Extension extension;
 
     private volatile WeldContainer container;
 
     private WeldInitiator(Weld weld, List<Object> instancesToInject,
-            Set<Class<? extends Annotation>> scopesToActivate) {
+            Set<Class<? extends Annotation>> scopesToActivate, Set<Bean<?>> beans) {
         this.instancesToInject = instancesToInject;
         this.scopesToActivate = scopesToActivate;
+        this.beans = beans;
         this.weld = weld;
-        if (hasScopesToActivate()) {
-            this.extension = new WeldJunit4Extension(this.scopesToActivate);
+        if (hasScopesToActivate() || hasBeansToAdd()) {
+            this.extension = new WeldJunit4Extension(this.scopesToActivate, this.beans);
             this.weld.addExtension(this.extension);
         } else {
             this.extension = null;
@@ -375,7 +398,7 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
 
     /**
      *
-     *  @return <code>true</code> if the container was initialized completely and is not shut down yet, <code>false</code> otherwise
+     * @return <code>true</code> if the container was initialized completely and is not shut down yet, <code>false</code> otherwise
      */
     public boolean isRunning() {
         return container.isRunning();
@@ -405,4 +428,8 @@ public class WeldInitiator implements TestRule, WeldInstance<Object>, ContainerI
         return scopesToActivate != null && !scopesToActivate.isEmpty();
     }
 
+    private boolean hasBeansToAdd() {
+        return beans != null && !beans.isEmpty();
+    }
+
 }
diff --git a/junit4/src/main/java/org/jboss/weld/junit4/WeldJunit4Extension.java b/junit4/src/main/java/org/jboss/weld/junit4/WeldJunit4Extension.java
index 06bb514..1e9f3c5 100644
--- a/junit4/src/main/java/org/jboss/weld/junit4/WeldJunit4Extension.java
+++ b/junit4/src/main/java/org/jboss/weld/junit4/WeldJunit4Extension.java
@@ -23,6 +23,7 @@ import java.util.Set;
 
 import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.Extension;
 
 /**
@@ -33,23 +34,33 @@ class WeldJunit4Extension implements Extension {
 
     private final Set<Class<? extends Annotation>> scopesToActivate;
 
+    private final Set<Bean<?>> beans;
+
     private final List<ContextImpl> contexts;
 
-    public WeldJunit4Extension(Set<Class<? extends Annotation>> scopesToActivate) {
+    WeldJunit4Extension(Set<Class<? extends Annotation>> scopesToActivate, Set<Bean<?>> beans) {
         this.scopesToActivate = scopesToActivate;
+        this.beans = beans;
         this.contexts = new ArrayList<>();
     }
 
     void afterBeandiscovery(@Observes AfterBeanDiscovery event) {
-        for (Class<? extends Annotation> scope : scopesToActivate) {
-            ContextImpl ctx = new ContextImpl(scope);
-            contexts.add(ctx);
-            event.addContext(ctx);
+        if (scopesToActivate != null) {
+            for (Class<? extends Annotation> scope : scopesToActivate) {
+                ContextImpl ctx = new ContextImpl(scope);
+                contexts.add(ctx);
+                event.addContext(ctx);
+            }
+        }
+        if (beans != null) {
+            for (Bean<?> bean : beans) {
+                event.addBean(bean);
+            }
         }
     }
 
     void activateContexts() {
-        if(contexts.isEmpty()) {
+        if (contexts.isEmpty()) {
             return;
         }
         for (ContextImpl context : contexts) {
@@ -58,7 +69,7 @@ class WeldJunit4Extension implements Extension {
     }
 
     void deactivateContexts() {
-        if(contexts.isEmpty()) {
+        if (contexts.isEmpty()) {
             return;
         }
         for (ContextImpl context : contexts) {
diff --git a/pom.xml b/pom.xml
index a6c14bb..4951c62 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>weld-junit-parent</artifactId>
    <packaging>pom</packaging>
@@ -41,6 +42,7 @@
       <!-- Versions -->
       <version.junit4>4.12</version.junit4>
       <version.weld>2.4.2.SP1</version.weld>
+      <version.mockito>2.7.19</version.mockito>
    </properties>
 
    <modules>
@@ -63,6 +65,13 @@
             <version>${version.junit4}</version>
          </dependency>
 
+         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${version.mockito}</version>
+            <scope>test</scope>
+         </dependency>
+
       </dependencies>
 
    </dependencyManagement>


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
new file mode 100644
index 0000000..1ab0dd8
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/AddBeanTest.java
@@ -0,0 +1,134 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2017, 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 static org.junit.Assert.assertEquals;
+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.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.util.TypeLiteral;
+
+import org.jboss.weld.junit4.MockBean;
+import org.jboss.weld.junit4.MockBean.CreateFunction;
+import org.jboss.weld.junit4.WeldInitiator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+public class AddBeanTest {
+
+    private static final AtomicInteger SEQUENCE = new AtomicInteger(0);
+
+    @Rule
+    public WeldInitiator weld = WeldInitiator.from(Blue.class)
+            .addBeans(MockBean.of(MyService.class, mock(MyService.class)), createListBean(),
+                    createSequenceBean(), createIdSupplierBean())
+            .build();
+
+    @SuppressWarnings("serial")
+    static Bean<?> createListBean() {
+        return MockBean.builder()
+                .types(new TypeLiteral<List<String>>() {}.getType())
+                .qualifiers(Meaty.Literal.INSTANCE)
+                .creating(
+                        // Mock object provided by Mockito
+                        when(mock(List.class).get(0)).thenReturn("42").getMock())
+                .build();
+    }
+
+    static Bean<?> createSequenceBean() {
+        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();
+                            }
+                        })
+                .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();
+    }
+
+    @Test
+    public void testBeansAdded() {
+        // Blue injects @Meaty List<String>
+        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++) {
+            assertEquals(Integer.valueOf(i),
+                    weld.select(Integer.class, Meaty.Literal.INSTANCE).get());
+        }
+
+        // Mock with default settings
+        MyService myService = weld.select(MyService.class).get();
+        myService.doBusiness("Adalbert");
+        Mockito.verify(myService, atLeastOnce()).doBusiness(anyString());
+
+        // Test applicaction scoped bean
+        assertEquals(weld.select(IdSupplier.class).get().getId(), weld.select(IdSupplier.class).get().getId());
+    }
+
+    interface MyService {
+
+        void doBusiness(String name);
+
+    }
+
+    static class IdSupplier {
+
+        private final String id;
+
+        public IdSupplier(String id) {
+            this.id = id;
+        }
+
+        String getId() {
+            return id;
+        }
+
+    }
+
+}
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/bean/Blue.java b/junit4/src/test/java/org/jboss/weld/junit4/bean/Blue.java
new file mode 100644
index 0000000..0d5d298
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/Blue.java
@@ -0,0 +1,35 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2017, 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.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+@ApplicationScoped
+public class Blue {
+
+    @Inject
+    @Meaty
+    private List<String> stringList;
+
+    public List<String> getStringList() {
+        return stringList;
+    }
+
+}
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/bean/Meaty.java b/junit4/src/test/java/org/jboss/weld/junit4/bean/Meaty.java
new file mode 100644
index 0000000..e063361
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/Meaty.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2017, 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 static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Qualifier;
+
+@Qualifier
+@Target({ TYPE, METHOD, PARAMETER, FIELD })
+@Retention(RUNTIME)
+public @interface Meaty {
+
+    @SuppressWarnings("all")
+    static class Literal extends AnnotationLiteral<Meaty> implements Meaty {
+
+        private static final long serialVersionUID = 1L;
+
+        public static final Meaty INSTANCE = new Literal();
+
+        private Literal() {
+        }
+
+    }
+
+}