Repository: weld/weld-junit (java)

Issue:
AbstractBuilder doesn't provide ability to add alternatives as well as beans
I suggest to add this method:
`public T addAlternatives(Class<?>... clz) {
            for(Class cl:clz){
                weld.addAlternative(cl);
            }
            return self();
        }`

Pull request:
MockBean - make it possible to specify the bean class
- also add convenient MockBean.Builder.selectedAlternative()
- resolves #22

Patch:
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java b/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
index 79d14ff..30577e9 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/AbstractWeldInitiator.java
@@ -89,7 +89,8 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
     protected volatile WeldContainer container;
 
     protected AbstractWeldInitiator(Weld weld, List<Object> instancesToInject, Set<Class<? extends Annotation>> scopesToActivate, Set<Bean<?>> beans,
-            Map<String, Object> resources, Function<InjectionPoint, Object> ejbFactory, Function<InjectionPoint, Object> persistenceUnitFactory, Function<InjectionPoint, Object> persistenceContextFactory) {
+            Map<String, Object> resources, Function<InjectionPoint, Object> ejbFactory, Function<InjectionPoint, Object> persistenceUnitFactory,
+            Function<InjectionPoint, Object> persistenceContextFactory) {
         this.instancesToInject = new ArrayList<>();
         for (Object instance : instancesToInject) {
             this.instancesToInject.add(createToInject(instance));
@@ -99,6 +100,14 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
         this.weld = weld;
         if (hasScopesToActivate() || hasBeansToAdd()) {
             this.extension = new WeldCDIExtension(this.scopesToActivate, this.beans);
+            for (Bean<?> bean : this.beans) {
+                if (bean instanceof MockBean) {
+                    MockBean<?> mockBean = (MockBean<?>) bean;
+                    if (mockBean.isAlternative() && mockBean.isSelectForSyntheticBeanArchive()) {
+                        this.weld.addAlternative(mockBean.getBeanClass());
+                    }
+                }
+            }
             this.weld.addExtension(this.extension);
         } else {
             this.extension = null;
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 ff7c80a..24a7ac4 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
@@ -32,6 +32,8 @@ import javax.enterprise.inject.spi.InjectionPoint;
 import javax.enterprise.inject.spi.PassivationCapable;
 import javax.enterprise.util.AnnotationLiteral;
 
+import org.jboss.weld.environment.se.Weld;
+
 /**
  * This custom {@link Bean} implementation is useful for mocking.
  * <p>
@@ -70,8 +72,8 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
     }
 
     /**
-     * A convenient method to create a {@link Bean} with default values (see also {@link #builder()}). Additionaly, the specified bean types are added to the set
-     * of bean types and {@link Bean#create(CreationalContext)} will always return the specified bean instance.
+     * A convenient method to create a {@link Bean} with default values (see also {@link #builder()}). Additionaly, the specified bean types are added to the
+     * set of bean types and {@link Bean#create(CreationalContext)} will always return the specified bean instance.
      *
      * @param beanInstance
      * @param beanTypes
@@ -87,6 +89,8 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
 
     private final boolean alternative;
 
+    private final boolean selectForSyntheticBeanArchive;
+
     private final String name;
 
     private final Set<Annotation> qualifiers;
@@ -101,19 +105,22 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
 
     private final String id;
 
-    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) {
+    private final Class<?> beanClass;
+
+    private MockBean(Class<?> beanClass, Set<Class<? extends Annotation>> stereotypes, boolean alternative, boolean selectForSyntheticBeanArchive, String name,
+            Set<Annotation> qualifiers, Set<Type> types, Class<? extends Annotation> scope, CreateFunction<T> createCallback,
+            DestroyFunction<T> destroyCallback) {
+        this.beanClass = beanClass;
         this.stereotypes = stereotypes;
         this.alternative = alternative;
+        this.selectForSyntheticBeanArchive = selectForSyntheticBeanArchive;
         this.name = name;
         this.qualifiers = qualifiers;
         this.types = types;
         this.scope = scope;
         this.createCallback = createCallback;
         this.destroyCallback = destroyCallback;
-        this.id = new StringBuilder().append(MockBean.class.getName()).append("_")
-                .append(SEQUENCE.incrementAndGet()).toString();
+        this.id = new StringBuilder().append(MockBean.class.getName()).append("_").append(SEQUENCE.incrementAndGet()).toString();
     }
 
     @Override
@@ -130,7 +137,7 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
 
     @Override
     public Class<?> getBeanClass() {
-        return WeldCDIExtension.class;
+        return beanClass;
     }
 
     @Override
@@ -173,6 +180,16 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
         return alternative;
     }
 
+    /**
+     * Initiator should use {@link #getBeanClass()} to select the alternative. Also the alternative should only be selected if {@link #isAlternative()} also
+     * returns <code>true</code>.
+     *
+     * @return <code>true</code> if the initiator should select the bean for the synthetic bean archive
+     */
+    boolean isSelectForSyntheticBeanArchive() {
+        return selectForSyntheticBeanArchive;
+    }
+
     @Override
     public String getId() {
         return id;
@@ -187,10 +204,14 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
      */
     public static class Builder<T> {
 
+        private Class<?> beanClass;
+
         private Set<Class<? extends Annotation>> stereotypes;
 
         private boolean alternative;
 
+        private boolean selectForSyntheticBeanArchive;
+
         private String name;
 
         private Set<Annotation> qualifiers;
@@ -211,6 +232,18 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
             this.scope = Dependent.class;
             this.types = new HashSet<>();
             this.types.add(Object.class);
+            this.beanClass = WeldCDIExtension.class;
+        }
+
+        /**
+         *
+         * @param beanClass
+         * @return self
+         * @see Bean#getBeanClass()
+         */
+        public Builder<T> beanClass(Class<?> beanClass) {
+            this.beanClass = beanClass;
+            return this;
         }
 
         /**
@@ -270,6 +303,40 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
             return this;
         }
 
+        /**
+         * The bean is an alternative and should be automatically selected for the synthetic bean archive.
+         *
+         * <p>
+         * Users are encouraged to specify {@link #beanClass(Class)} when using this method. The bean class is used to determine which alternative beans are
+         * selected for a bean archive. By default, all mock beans share the same bean class - {@code org.jboss.weld.junit.WeldCDIExtension}.
+         * </p>
+         *
+         * @param value
+         * @return self
+         * @see Bean#isAlternative()
+         * @see Weld#addAlternative(Class)
+         * @see #selectedAlternative(Class)
+         */
+        public Builder<T> selectedAlternative() {
+            alternative(true);
+            this.selectForSyntheticBeanArchive = true;
+            return this;
+        }
+
+        /**
+         * The bean has the given bean class, is an alternative and should be automatically selected for the synthetic bean archive.
+         *
+         * @param beanClass
+         * @return self
+         * @see #selectedAlternative()
+         * @see #beanClass(Class)
+         */
+        public Builder<T> selectedAlternative(Class<?> beanClass) {
+            selectedAlternative();
+            beanClass(beanClass);
+            return this;
+        }
+
         /**
          *
          * @param stereotypes
@@ -333,8 +400,8 @@ public class MockBean<T> implements Bean<T>, PassivationCapable {
             if (qualifiers.size() == 1) {
                 qualifiers.add(DefaultLiteral.INSTANCE);
             }
-            return new MockBean<>(stereotypes, alternative, name, qualifiers, types, scope,
-                    createCallback, destroyCallback);
+            return new MockBean<>(beanClass, stereotypes, alternative, selectForSyntheticBeanArchive, name, qualifiers, types, scope, createCallback,
+                    destroyCallback);
         }
 
     }


Test patch:
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/bean/AlternativeMockBeanTest.java b/junit4/src/test/java/org/jboss/weld/junit4/bean/AlternativeMockBeanTest.java
new file mode 100644
index 0000000..a7c9215
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/bean/AlternativeMockBeanTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 javax.enterprise.context.Dependent;
+
+import org.jboss.weld.junit.MockBean;
+import org.jboss.weld.junit4.WeldInitiator;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+public class AlternativeMockBeanTest {
+
+    @Rule
+    public WeldInitiator weld = WeldInitiator.from(SimpleService.class)
+            .addBeans(MockBean.builder().types(MyService.class).selectedAlternative(CoolService.class).create(c -> new CoolService()).build()).build();
+
+    @Test
+    public void testAlternativeBeanSelected() {
+        assertEquals(1000, weld.select(MyService.class).get().doBusiness());
+    }
+
+    interface MyService {
+
+        int doBusiness();
+
+    }
+
+    @Dependent
+    static class SimpleService implements MyService {
+
+        @Override
+        public int doBusiness() {
+            return 0;
+        }
+    }
+
+    static class CoolService implements MyService {
+
+        @Override
+        public int doBusiness() {
+            return 1000;
+        }
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/bean/AlternativeMockBeanTest.java b/junit5/src/test/java/org/jboss/weld/junit5/bean/AlternativeMockBeanTest.java
new file mode 100644
index 0000000..c965c46
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/bean/AlternativeMockBeanTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.junit5.bean;
+
+import javax.enterprise.context.Dependent;
+
+import org.jboss.weld.junit.MockBean;
+import org.jboss.weld.junit5.EnableWeld;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldSetup;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+@EnableWeld
+public class AlternativeMockBeanTest {
+
+    @WeldSetup
+    public WeldInitiator weld = WeldInitiator.from(SimpleService.class)
+            .addBeans(MockBean.builder().types(MyService.class).selectedAlternative().beanClass(CoolService.class).create(c -> new CoolService()).build())
+            .build();
+
+    @Test
+    public void testAlternativeBeanSelected() {
+        Assertions.assertEquals(1000, weld.select(MyService.class).get().doBusiness());
+    }
+
+    interface MyService {
+
+        int doBusiness();
+
+    }
+
+    @Dependent
+    static class SimpleService implements MyService {
+
+        @Override
+        public int doBusiness() {
+            return 0;
+        }
+    }
+
+    static class CoolService implements MyService {
+
+        @Override
+        public int doBusiness() {
+            return 1000;
+        }
+    }
+
+}