Repository: weld/weld-junit
Pull request #59 (2018-09-22 07:16:37)

Allow excluding `OverrideBean` classes
Reinstate `OverrideBean`’s ability to exclude the class its overriding from scanning; now as an annotation option. This allows mock producers, such as:
```
@Produces
@OverrideBean(exclude = true)
@Mock
SomethingWithInjectedDeps something;
```
To mock the class, while not requiring the mocking of its dependencies.

manovotn: Hello,

from our docs, we state that `OverrideBean` is an always enabled alternative stereotype.
What you describe in this PR sounds more like what [specialization](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#specialization) is in CDI, am I right?

What is the use case where you need to get rid of the original bean instead of just overriding it? To disable some producer on it?
kdubb: In the example code I provided, the idea is that we want to mock `SomethingWithInjectedDeps`. As its name attempts to imply, it has CDI dependencies (e.g. `@Inject`) that need to be satisfied.  Since we are _mocking_ the instance we don't care about those dependencies.   The way `ClassScanning` and `OverrieBean` currently work, it adds the class `SomethingWithInjectedDeps` and searches it for dependencies.

The end result is that, with the current code, even though I am mocking the object I still have to satisfy all the dependencies of `SomethingWithInjectedDeps` because its added as a normal bean and according to CDI it _may_ be injected at some point.

Allowing exclusion of `SomethingWithInjectedDeps` via `OverrideBean` allows us to produce a mocked version of `SomethingWithInjectedDeps` without concerning ourselves with any of its dependencies.
manovotn: I see, thanks for explanation.

> The end result is that, with the current code, even though I am mocking the object I still have to satisfy all the dependencies of `SomethingWithInjectedDeps` because its added as a normal bean and according to CDI it may be injected at some point.

That's designed behaviour though - `OverrideBean` is a parallel approach to alternatives. For instance, you may have a producer on a such bean and you want that one to still work.

Rather then twisting this approach with alternatives, I think it would be better to have a look a specialization - can't your mock specialize the original bean? Then you could just add the new bean and it would work out of the box without additional changes.

If the answer is no, then we will probably do something as you suggested, only I would aim to document it as `OverrideBean` working as specialization (and naming the parameter in accordance with that).
kdubb: @manovotn After trying numerous things related to `@Alternative` and `@Specializes`. I realized that the definition of `@OverrideBean` as I had envisioned it was co-opted into basically a `@EnableAlternative` annotation.

We already have a more "CDI" way of enabling alternatives via `@EnableAlternatives`. The reason I say it is a more "CDI" way of doing things is because in CDI alternatives are generally enabled _externally_ (e.g. via `beans.xml`).  So using an external annotation like `@EnableAlternatives` is very similar and easy.

I suggest we return `@OverrideBean` to its original intention of _*replacing*_ the definition of a bean.  The name includes _override_ which implies something beyond enabling an alternative; again you can easily enable alternatives via annotation.  To _replace_ a bean means we must exclude it from scanning (so that CDI never sees the original bean), which is what my original PR did when using `@OverrideBean`. 

Interestingly another thing I noticed is that `@OverrideBean`, strictly speaking, need not be an enabled alternative stereotype as it is now.  Since we are excluding the bean class from scanning CDI never sees the original and therefore need not work out which alternative is correct.  Although, if the original class is added via other annotations (e.g. `@AddBeanClasses`), leaving the annotation be an enabled alternative marker ensures the bean marked with it is always used.

Once we agree on the correct direction I'll submit a PR to implement the changes.  Our current test set requires this exclusion functionality.  I only recently discovered the differences when I attempted to switch back to the latest published version.
kdubb: FYI, I forgot to explicitly mention why `@Specializes` doesn't work for this.

1. `@Specializes` requires that you subclass the original bean class or the class in which its producer method is defined.
  * This can require changes to non-test code and only succeeds in skipping dependency requirements when the bean is using constructor injection.
1. `@Specializes` doesn't allow you to replace a bean defined by class annotation with a bean produced by a field or method; given the above requirement, it simply doesn't work that way.
  * Using `@OverrideBean` with exclusion specifically allows this and provides easy "no change" mocking as demonstrated previously.
manovotn: Hmm I can see why specialization doesn't quite cut it for you.

It is true that the functionality of `OverrideBean` overlaps with the simpler approach of declaring an enabled alternative in the test.
What's bugging me is that this will break any app possibly using `OverrideBean` as a selected alternative. In that case, having a separate annotation for what you are trying to achieve might be safer?

Also, your solution of excluding the classes might have certain flaws - it operates on return types which mean that should you return interface as a type, any actual bean with the implementation type will still be "discovered and active", right? That is actually one of the reasons why true specialization requires subclassing as without it you bump into weird situations like this.

@mkouba @nziakova thoughts?
mkouba: Hey @kdubb!

1. Why do you actually add the bean class you're trying to "override" to your test configuration? If you want to mock a bean backed by a class you don't really need to add the bean class to the bean archive.
2. A simple example would be really helpful. I don't quite understand the use case. 
kdubb: @mkouba The example at the beginning __is__ the example.  I'm not adding the bean I want to override manually but the auto-configuration scanning picks it up due to it's `@Produces` annotation.  The annotation is needed to "disable" adding the overridden class via scanning.
mkouba: @kdubb Ok, so I think I finally understand your use case. I wonder if we could add a special annotation like `@Exclude` to handle a more general use case -> to specify the set of bean classes that shoudn't be added by `WeldJunit5AutoExtension`, i.e. your example could be rewritten as follows:

```java
@EnableAutoWeld
@Exclude(Foo.class) // -> Foo is normally added due to fakeFoo producer field
class ProducesAlternativeTest {

  @Produces
  @Mock
  Foo fakeFoo = new Foo("non-baz");
}
```
kdubb: @mkouba Due to the seeming resistance to reverting `@OverrideBean` to what I originally intended,  I've returned `@OverrideBean` to its previous released state and added `ProducesOverride` in its place & added proper Javadoc to it.

Using something like your suggested `@Exclude(classes)` means one has to collect all the overrides in one place at the top of the test. It's a bit easier to just add `@ProducesOverride` to the producer method or producer field that you want to override (see aforementioned Javadoc).

**Note** At this point `OverrideBean`'s documentation is essentially wrong. It uses the word "override" to reference activating an alternative; which, as I've shown, doesn't _override_ anything.  Also, there is already a method to do this with `EnableAlternatives/EnableAlternativeStereotypes` which more closely match CDI's configuration style.  It would seem deprecating and removing `@OverrideBean` in the future would be in order.

Patch:
diff --git a/.gitignore b/.gitignore
index b4b139f..1c979b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
 /.classpath
 /.project
 /.settings/
+/.idea
+*.iml
 
diff --git a/junit-common/.gitignore b/junit-common/.gitignore
index 8bd3a05..8841452 100644
--- a/junit-common/.gitignore
+++ b/junit-common/.gitignore
@@ -2,3 +2,5 @@
 /.settings/
 /.classpath
 /.project
+/.idea
+*.iml
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 cea050a..9cdbfbe 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
@@ -99,6 +99,7 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
         this.beans = beans;
         this.weld = weld;
         boolean hasMockInterceptor = false;
+        boolean dummyBeanAdded = false;
         if (hasScopesToActivate() || hasBeansToAdd()) {
             this.extension = new WeldCDIExtension(this.scopesToActivate, this.beans);
             for (Bean<?> bean : this.beans) {
@@ -106,6 +107,12 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
                     MockBean<?> mockBean = (MockBean<?>) bean;
                     if (mockBean.isAlternative() && mockBean.isSelectForSyntheticBeanArchive()) {
                         this.weld.addAlternative(mockBean.getBeanClass());
+                        if (!dummyBeanAdded) {
+                            // by adding a dummy bean we make sure that synthetic archive gets created even if
+                            // the enabled alternative was the only bean added
+                            this.weld.addBeanClass(Object.class);
+                            dummyBeanAdded = true;
+                        }
                     }
                 } else if (bean instanceof MockInterceptor && ((MockInterceptor) bean).hasDefaultBeanClass()) {
                     hasMockInterceptor = true;
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/MockEjbInjectionServices.java b/junit-common/src/main/java/org/jboss/weld/junit/MockEjbInjectionServices.java
index 8c6aa81..be0027b 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/MockEjbInjectionServices.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/MockEjbInjectionServices.java
@@ -53,7 +53,7 @@ public class MockEjbInjectionServices implements EjbInjectionServices {
         };
     }
 
-    @Override
+    // only for Weld 2
     public Object resolveEjb(InjectionPoint injectionPoint) {
         return ejbFactory.apply(injectionPoint);
     }
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/MockJpaInjectionServices.java b/junit-common/src/main/java/org/jboss/weld/junit/MockJpaInjectionServices.java
index 3b7d029..2167916 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/MockJpaInjectionServices.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/MockJpaInjectionServices.java
@@ -64,7 +64,7 @@ public class MockJpaInjectionServices implements JpaInjectionServices {
         };
     }
 
-    @Override
+    // only for Weld 2
     public EntityManager resolvePersistenceContext(InjectionPoint injectionPoint) {
         if (persistenceContextFactory == null) {
             throw new IllegalStateException("Persistent context factory not set, cannot resolve injection point: " + injectionPoint);
@@ -76,7 +76,7 @@ public class MockJpaInjectionServices implements JpaInjectionServices {
         throw new IllegalStateException("Not an EntityManager instance: " + context);
     }
 
-    @Override
+    // only for Weld 2
     public EntityManagerFactory resolvePersistenceUnit(InjectionPoint injectionPoint) {
         if (persistenceUnitFactory == null) {
             throw new IllegalStateException("Persistent unit factory not set, cannot resolve injection point: " + injectionPoint);
diff --git a/junit-common/src/main/java/org/jboss/weld/junit/MockResourceInjectionServices.java b/junit-common/src/main/java/org/jboss/weld/junit/MockResourceInjectionServices.java
index 0fbbb0b..2b28456 100644
--- a/junit-common/src/main/java/org/jboss/weld/junit/MockResourceInjectionServices.java
+++ b/junit-common/src/main/java/org/jboss/weld/junit/MockResourceInjectionServices.java
@@ -49,7 +49,7 @@ public class MockResourceInjectionServices implements ResourceInjectionServices
         this.resources = ImmutableMap.copyOf(resources);
     }
 
-    @Override
+    // only for Weld 2
     public Object resolveResource(InjectionPoint injectionPoint) {
         Resource resource = getResourceAnnotation(injectionPoint);
         if (resource == null) {
@@ -83,7 +83,7 @@ public class MockResourceInjectionServices implements ResourceInjectionServices
         throw new UnsupportedOperationException();
     }
 
-    @Override
+    // only for Weld 2
     public Object resolveResource(String jndiName, String mappedName) {
         throw new UnsupportedOperationException();
     }
diff --git a/junit4/.gitignore b/junit4/.gitignore
index 8bd3a05..8841452 100644
--- a/junit4/.gitignore
+++ b/junit4/.gitignore
@@ -2,3 +2,5 @@
 /.settings/
 /.classpath
 /.project
+/.idea
+*.iml
diff --git a/junit4/pom.xml b/junit4/pom.xml
index 7074969..b584bea 100644
--- a/junit4/pom.xml
+++ b/junit4/pom.xml
@@ -45,22 +45,6 @@
 
    </dependencies>
 
-   <build>
-      <plugins>
-         <plugin>
-            <artifactId>maven-surefire-plugin</artifactId>
-            <configuration>
-               <systemProperties>
-                  <property>
-                     <name>java.util.logging.config.file</name>
-                     <value>${project.build.testOutputDirectory}/logging.properties</value>
-                  </property>
-               </systemProperties>
-            </configuration>
-         </plugin>
-      </plugins>
-   </build>
-
    <profiles>
 
       <profile>
diff --git a/junit5/.gitignore b/junit5/.gitignore
index 8bd3a05..8841452 100644
--- a/junit5/.gitignore
+++ b/junit5/.gitignore
@@ -2,3 +2,5 @@
 /.settings/
 /.classpath
 /.project
+/.idea
+*.iml
diff --git a/junit5/README.md b/junit5/README.md
index 044a683..273849c 100644
--- a/junit5/README.md
+++ b/junit5/README.md
@@ -314,7 +314,7 @@ class AddBeanTest {
 Sometimes it might be useful to add a mock interceptor, e.g. if an interceptor implementation requires some environment-specific features.
 For this use case the `org.jboss.weld.junit.MockInterceptor` is a perfect match:
 
-```
+```java
 @FooBinding
 class Foo {
    boolean ping() {
diff --git a/junit5/pom.xml b/junit5/pom.xml
index ad4f8ea..05454c8 100644
--- a/junit5/pom.xml
+++ b/junit5/pom.xml
@@ -48,37 +48,9 @@
          <artifactId>hibernate-jpa-2.1-api</artifactId>
          <scope>test</scope>
       </dependency>
-
+      
    </dependencies>
 
-   <build>
-      <pluginManagement>
-         <plugins>
-            <plugin>
-               <groupId>org.apache.maven.plugins</groupId>
-               <artifactId>maven-surefire-plugin</artifactId>
-               <dependencies>
-                  <!-- Surefire doesn't know JUnit 5 provider yet, we need
-                     to add it here -->
-                  <dependency>
-                     <groupId>org.junit.platform</groupId>
-                     <artifactId>junit-platform-surefire-provider</artifactId>
-                     <version>${version.junit.platform}</version>
-                  </dependency>
-               </dependencies>
-               <configuration>
-                  <systemProperties>
-                     <property>
-                        <name>java.util.logging.config.file</name>
-                        <value>${project.build.testOutputDirectory}/logging.properties</value>
-                     </property>
-                  </systemProperties>
-               </configuration>
-            </plugin>
-         </plugins>
-      </pluginManagement>
-   </build>
-
    <profiles>
 
       <profile>
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 63f33ac..3335f9f 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
@@ -20,13 +20,13 @@ import org.jboss.weld.environment.se.Weld;
 import org.jboss.weld.util.collections.ImmutableList;
 import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.api.extension.AfterAllCallback;
-import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.jupiter.api.extension.ParameterContext;
 import org.junit.jupiter.api.extension.ParameterResolutionException;
 import org.junit.jupiter.api.extension.ParameterResolver;
-import org.junit.jupiter.api.extension.TestInstancePostProcessor;
 
 import javax.enterprise.inject.spi.BeanManager;
 import java.lang.annotation.Annotation;
@@ -52,7 +52,8 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
 import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;
 
 /**
- * JUnit 5 extension allowing to bootstrap Weld SE container for each @Test method and tear it down afterwards. Also allows to
+ * JUnit 5 extension allowing to bootstrap Weld SE container for each @Test method (or per once per test class
+ * if running {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS}) and tear it down afterwards. Also allows to
  * inject CDI beans as parameters to @Test methods and resolves all @Inject fields in test class.
  *
  * <p>
@@ -80,11 +81,29 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;
  * @see EnableWeld
  * @see WeldJunitEnricher
  */
-public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback, TestInstancePostProcessor, AfterTestExecutionCallback, ParameterResolver {
+public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
+        BeforeEachCallback, AfterEachCallback, ParameterResolver {
 
     // global system property
     public static final String GLOBAL_EXPLICIT_PARAM_INJECTION = "org.jboss.weld.junit5.explicitParamInjection";
 
+    private static void storeExplicitParamResolutionInformation(ExtensionContext ec) {
+        // check system property which may have set the global explicit param injection
+        Boolean globalSettings = Boolean.valueOf(System.getProperty(GLOBAL_EXPLICIT_PARAM_INJECTION, "false"));
+        if (globalSettings) {
+            setExplicitInjectionInfoToStore(ec, true);
+            return;
+        }
+        // check class-level annotation
+        for (Annotation annotation : ec.getRequiredTestClass().getAnnotations()) {
+            if (annotation.annotationType().equals(ExplicitParamInjection.class)) {
+                setExplicitInjectionInfoToStore(ec, true);
+                break;
+            }
+        }
+
+    }
+
     @Override
     public void afterAll(ExtensionContext context) throws Exception {
         if (determineTestLifecycle(context).equals(PER_CLASS)) {
@@ -100,81 +119,17 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
             ServiceLoader.load(WeldJunitEnricher.class).forEach(enrichers::add);
             setEnrichersToStore(context, enrichers.build());
         }
+        // if the lifecycle is per-class, then we want to start container here
+        starWeldContainerIfAppropriate(PER_CLASS, context);
     }
 
     @Override
-    public void afterTestExecution(ExtensionContext context) throws Exception {
+    public void afterEach(ExtensionContext context) throws Exception {
         if (determineTestLifecycle(context).equals(PER_METHOD)) {
             getInitiatorFromStore(context).shutdownWeld();
         }
     }
 
-    @Override
-    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
-        // 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;
-        // 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
-                        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
-                            field.setAccessible(true);
-                            return null;
-                        });
-                        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());
-                    }
-                }
-            }
-        }
-        // 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) {
-            Weld weld = WeldInitiator.createWeld();
-            WeldInitiator.Builder builder = WeldInitiator.from(weld);
-
-            weldInit(testInstance, context, weld, builder);
-
-            // Apply discovered enrichers
-            for (WeldJunitEnricher enricher : getEnrichersFromStore(context)) {
-                String property = System.getProperty(enricher.getClass().getName());
-                if (property == null || Boolean.parseBoolean(property)) {
-                    enricher.enrich(testInstance, context, weld, builder);
-                }
-            }
-
-            initiator = builder.build();
-        }
-        setInitiatorToStore(context, initiator);
-
-        // and finally, init Weld
-        setContainerToStore(context, initiator.initWeld(testInstance));
-    }
-
     protected void weldInit(Object testInstance, ExtensionContext context, Weld weld, WeldInitiator.Builder weldInitiatorBuilder) {
         weld.addPackage(false, testInstance.getClass());
     }
@@ -185,7 +140,7 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
         if (getContainerFromStore(extensionContext) != null) {
             List<Annotation> qualifiers = resolveQualifiers(parameterContext, getContainerFromStore(extensionContext).getBeanManager());
             return getContainerFromStore(extensionContext)
-                .select(parameterContext.getParameter().getType(), qualifiers.toArray(new Annotation[qualifiers.size()])).get();
+                    .select(parameterContext.getParameter().getType(), qualifiers.toArray(new Annotation[qualifiers.size()])).get();
         }
         return null;
     }
@@ -202,7 +157,7 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
             return false;
         } else {
             return getContainerFromStore(extensionContext).select(parameterContext.getParameter().getType(), qualifiers.toArray(new Annotation[qualifiers.size()]))
-                .isResolvable();
+                    .isResolvable();
         }
     }
 
@@ -240,21 +195,81 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
         }
     }
 
-    private static void storeExplicitParamResolutionInformation(ExtensionContext ec) {
-        // check system property which may have set the global explicit param injection
-        Boolean globalSettings = Boolean.valueOf(System.getProperty(GLOBAL_EXPLICIT_PARAM_INJECTION, "false"));
-        if (globalSettings) {
-            setExplicitInjectionInfoToStore(ec, true);
-            return;
-        }
-        // check class-level annotation
-        for (Annotation annotation : ec.getRequiredTestClass().getAnnotations()) {
-            if (annotation.annotationType().equals(ExplicitParamInjection.class)) {
-                setExplicitInjectionInfoToStore(ec, true);
-                break;
+    @Override
+    public void beforeEach(ExtensionContext extensionContext) throws Exception {
+        starWeldContainerIfAppropriate(PER_METHOD, extensionContext);
+    }
+
+    private void starWeldContainerIfAppropriate(TestInstance.Lifecycle expectedLifecycle, ExtensionContext context) throws Exception {
+        // is the lifecycle is what we expect it to be, start Weld container
+        if (determineTestLifecycle(context).equals(expectedLifecycle)) {
+            Object testInstance = context.getTestInstance().orElseGet(null);
+            if (testInstance == null) {
+                throw new IllegalStateException("ExtensionContext.getTestInstance() returned empty Optional!");
             }
-        }
 
-    }
+            // 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;
+            // 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
+                            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+                                field.setAccessible(true);
+                                return null;
+                            });
+                            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());
+                        }
+                    }
+                }
+            }
+            // 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) {
+                Weld weld = WeldInitiator.createWeld();
+                WeldInitiator.Builder builder = WeldInitiator.from(weld);
 
+                weldInit(testInstance, context, weld, builder);
+
+                // Apply discovered enrichers
+                for (WeldJunitEnricher enricher : getEnrichersFromStore(context)) {
+                    String property = System.getProperty(enricher.getClass().getName());
+                    if (property == null || Boolean.parseBoolean(property)) {
+                        enricher.enrich(testInstance, context, weld, builder);
+                    }
+                }
+
+                initiator = builder.build();
+            }
+            setInitiatorToStore(context, initiator);
+
+            // and finally, init Weld
+            setContainerToStore(context, initiator.initWeld(testInstance));
+        }
+    }
 }
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
index 2def830..815019f 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
@@ -67,24 +67,37 @@ import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
  */
 class ClassScanning {
 
-    public static void scanForRequiredBeanClass(Class<?> testClass, Weld weld, boolean explicitInjection) {
+    static void scanForRequiredBeanClass(Class<?> testClass, Weld weld, boolean explicitInjection) {
 
         List<Class<?>> classesToProcess = new ArrayList<>();
         classesToProcess.add(testClass);
 
         Set<Class<?>> foundClasses = new HashSet<>();
+        Set<Type> excludedBeanTypes = new HashSet<>();
+        Set<Class<?>> excludedBeanClasses = new HashSet<>();
 
         while (!classesToProcess.isEmpty()) {
 
             Class<?> currClass = classesToProcess.remove(0);
 
-            if (foundClasses.contains(currClass) || currClass.isPrimitive() || currClass.isSynthetic() || currClass.getName().startsWith("java") || currClass.getName().startsWith("sun")) {
+            if (foundClasses.contains(currClass) ||
+                    excludedBeanTypes.contains(currClass) || excludedBeanClasses.contains(currClass) ||
+                    currClass.isPrimitive() || currClass.isSynthetic() ||
+                    currClass.getName().startsWith("java") || currClass.getName().startsWith("sun")) {
                 continue;
             }
 
             foundClasses.add(currClass);
 
-            findAnnotatedFields(currClass, Object.class, Inject.class).stream()
+            findAnnotatedFields(currClass, ExcludeBean.class).stream()
+                    .map(Field::getType)
+                    .forEach(excludedBeanTypes::add);
+
+            AnnotationSupport.findAnnotatedMethods(currClass, ExcludeBean.class, HierarchyTraversalMode.BOTTOM_UP).stream()
+                    .map(Method::getReturnType)
+                    .forEach(excludedBeanTypes::add);
+
+            findAnnotatedFields(currClass, Inject.class).stream()
                     .map(Field::getType)
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
@@ -97,7 +110,7 @@ class ClassScanning {
                     .flatMap(cons -> getExecutableParameterTypes(cons, weld, explicitInjection).stream())
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
-            findAnnotatedFields(currClass, Object.class, Produces.class).stream()
+            findAnnotatedFields(currClass, Produces.class).stream()
                     .map(Field::getType)
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
@@ -172,6 +185,11 @@ class ClassScanning {
                     .distinct()
                     .forEach(weld::addAlternativeStereotype);
 
+            AnnotationSupport.findRepeatableAnnotations(currClass, ExcludeBeanClasses.class).stream()
+                    .flatMap(ann -> stream(ann.value()))
+                    .distinct()
+                    .forEach(excludedBeanClasses::add);
+
         }
 
         foundClasses.add(testClass);
@@ -184,6 +202,7 @@ class ClassScanning {
 
         }
 
+        weld.addExtension(new ExcludedBeansExtension(excludedBeanTypes, excludedBeanClasses));
     }
 
     private static void addClassesToProcess(Collection<Class<?>> classesToProcess, Type type) {
@@ -248,12 +267,12 @@ class ClassScanning {
 
     private static List<Field> findAllFieldsInHierarchy(Class<?> clazz) {
         Preconditions.notNull(clazz, "Class must not be null");
-        List<Field> localFields = getDeclaredFields(clazz).stream().filter((field) -> {
-            return !field.isSynthetic();
-        }).collect(Collectors.toList());
-        List<Field> superclassFields = getSuperclassFields(clazz).stream().filter((field) -> {
-            return !isMethodShadowedByLocalFields(field, localFields);
-        }).collect(Collectors.toList());
+        List<Field> localFields = getDeclaredFields(clazz).stream().
+                filter((field) -> !field.isSynthetic())
+                .collect(Collectors.toList());
+        List<Field> superclassFields = getSuperclassFields(clazz).stream()
+                .filter((field) -> !isMethodShadowedByLocalFields(field, localFields))
+                .collect(Collectors.toList());
         List<Field> methods = new ArrayList<>();
         methods.addAll(superclassFields);
         methods.addAll(localFields);
@@ -271,26 +290,25 @@ class ClassScanning {
     }
 
     private static boolean isMethodShadowedByLocalFields(Field field, List<Field> localFields) {
-        return localFields.stream().anyMatch((local) -> {
-            return isFieldShadowedBy(field, local);
-        });
+        return localFields.stream()
+                .anyMatch((local) -> isFieldShadowedBy(field, local));
     }
 
     private static boolean isFieldShadowedBy(Field upper, Field lower) {
         return upper.getType().equals(lower.getType());
     }
 
-    public static List<Field> findAnnotatedFields(Class<?> clazz, Class<?> fieldType, Class<? extends Annotation> annotationType) {
-        return getDeclaredFields(clazz).stream().filter((field) -> {
-            return fieldType.isAssignableFrom(field.getType()) && isAnnotated(field, annotationType);
-        }).collect(CollectionUtils.toUnmodifiableList());
+    private static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType) {
+        return getDeclaredFields(clazz).stream()
+                .filter((field) -> isAnnotated(field, annotationType))
+                .collect(CollectionUtils.toUnmodifiableList());
     }
 
     private static List<Constructor<?>> getDeclaredConstructors(Class<?> clazz) {
         return asList(clazz.getDeclaredConstructors());
     }
 
-    public static Optional<Constructor<?>> findFirstAnnotatedConstructor(Class<?> clazz, Class<? extends Annotation> annotationType) {
+    private static Optional<Constructor<?>> findFirstAnnotatedConstructor(Class<?> clazz, Class<? extends Annotation> annotationType) {
 
         Optional<Constructor<?>> found = getDeclaredConstructors(clazz).stream()
                 .filter((cons) -> isAnnotated(cons, annotationType))
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBean.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBean.java
new file mode 100644
index 0000000..3705467
--- /dev/null
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBean.java
@@ -0,0 +1,47 @@
+package org.jboss.weld.junit5.auto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code ExcludeBean} excludes a class, or all classes within a hierarchy, that include a bean defining annotation (
+ * e.g. scope) from automatic discovery. This can be helpful to allow replacing a bean class with a different
+ * implementation; typically a mock.
+ *
+ * The type of bean to exclude is implied by the annotated field's type or annotated method's return type. If the type
+ * is a base class or interface all beans in the hierarchy will be excluded.
+ *
+ * NOTE: This annotation will only exclude beans defined by class annotations. It will not exclude beans of the
+ * implied type that are defined by {@link javax.enterprise.inject.Produces producer methods or fields}. Also, the
+ * current implementation excludes beans based on type, disregarding any qualifiers that are specified.
+ *
+ * Example:
+ * <pre>
+ * &#64;EnableAutoWeld
+ * class TestSomeFoo {
+ *
+ *   &#64;Inject
+ *   SomeFoo someFoo;   // SomeFoo depends upon application scoped bean Foo
+ *
+ *   &#64;Produces
+ *   &#64;ExcludeBean   // Excludes Foo bean class from automatic discovery
+ *   Foo mockFoo = mock(Foo.class);  // mockFoo is now produced in place of original Foo impl
+ *
+ *   &#64;Test
+ *   void test(Foo myFoo) {
+ *     assertNotNull(myFoo);
+ *     assertEquals(myFoo.getBar(), "mock-foo");
+ *   }
+ * }
+ * </pre>
+ *
+ * @see ExcludeBeanClasses
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+@Inherited
+public @interface ExcludeBean {
+}
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBeanClasses.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBeanClasses.java
new file mode 100644
index 0000000..4a8b534
--- /dev/null
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludeBeanClasses.java
@@ -0,0 +1,54 @@
+package org.jboss.weld.junit5.auto;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@code ExcludeBeanClasses} excludes a set of classes with bean defining annotations (e.g. scopes) from automatic
+ * discovery. This can be helpful to allow replacing bean classes with a different implementation; typically a mock.
+ *
+ * NOTE: This annotation will only exclude beans defined by class annotations. It will not exclude beans of the
+ * specified type that are defined by {@link javax.enterprise.inject.Produces producer methods or fields}.
+ *
+ * Example:
+ * <pre>
+ * &#64;EnableAutoWeld
+ * &#64;ExcludeBeanClasses(Foo.class) // Excludes Foo bean class from automatic discovery
+ * class TestSomeFoo {
+ *
+ *   &#64;Inject
+ *   SomeFoo someFoo;   // SomeFoo depends upon application scoped bean Foo
+ *
+ *   &#64;Produces
+ *   Foo mockFoo = mock(Foo.class);  // mockFoo is now produced in place of original Foo impl
+ *
+ *   &#64;Test
+ *   void test(Foo myFoo) {
+ *     assertNotNull(myFoo);
+ *     assertEquals(myFoo.getBar(), "mock-foo");
+ *   }
+ * }
+ * </pre>
+
+ * @see ExcludeBean
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+@Repeatable(ExcludeBeanClasses.All.class)
+public @interface ExcludeBeanClasses {
+
+    Class<?>[] value();
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    @Inherited
+    @interface All {
+        ExcludeBeanClasses[] value();
+    }
+
+}
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludedBeansExtension.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludedBeansExtension.java
new file mode 100644
index 0000000..4093353
--- /dev/null
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ExcludedBeansExtension.java
@@ -0,0 +1,59 @@
+/*
+ * 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.auto;
+
+import javax.enterprise.context.NormalScope;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+import javax.enterprise.inject.spi.WithAnnotations;
+import javax.inject.Scope;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+
+/**
+ * Extension class that ensures selected classes are excluded as
+ * beans.
+ */
+public class ExcludedBeansExtension implements Extension {
+
+    private Set<Type> excludedBeanTypes;
+    private Set<Class<?>> excludedBeanClasses;
+
+    ExcludedBeansExtension(Set<Type> excludedBeanTypes, Set<Class<?>> excludedBeanClasses) {
+        this.excludedBeanTypes = excludedBeanTypes;
+        this.excludedBeanClasses = excludedBeanClasses;
+    }
+
+    <T> void excludeBeans(@Observes @WithAnnotations({Scope.class, NormalScope.class}) ProcessAnnotatedType<T> pat) {
+
+        if (excludedBeanClasses.contains(pat.getAnnotatedType().getJavaClass())) {
+            pat.veto();
+            return;
+        }
+
+        Set<Type> typeClosure = pat.getAnnotatedType().getTypeClosure();
+        for (Type excludedBeanType : excludedBeanTypes) {
+            if (typeClosure.contains(excludedBeanType)) {
+                pat.veto();
+                return;
+            }
+        }
+    }
+
+}
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/OverrideBean.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/OverrideBean.java
deleted file mode 100644
index 2986c85..0000000
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/OverrideBean.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.auto;
-
-
-import javax.enterprise.inject.Alternative;
-import javax.enterprise.inject.Stereotype;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-
-/**
- * {@code @OverrideBean} is, strictly speaking, an always enabled alternative stereotype.
- * It is typically used along with {@code @javax.enterprise.inject.Produces}.
- *
- * It "overrides a bean" that <i>may</i> otherwise be included in the container by providing a new bean (via producer) and
- * selecting it.
- *
- * Here is how such a usage can look like:
- * <pre>
- * &#64;EnableAutoWeld
- * &#64;AddPackages(Foo.class) // this brings in the *original* Foo impl you want to override
- * class OverrideFooTest {
- * 
- *   &#64;Produces
- *   &#64;OverrideBean
- *   Foo fakeFoo = new Foo("non-baz");
- * 
- *   &#64;Test
- *   void test(Foo myFoo) {
- *     assertNotNull(myFoo);
- *     assertEquals(myFoo.getBar(), "non-baz");
- *   }
- * }
- * </pre>
- */
-@Stereotype
-@Alternative
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD, ElementType.METHOD})
-public @interface OverrideBean {
-}
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
index d080f28..2c1042b 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
@@ -18,14 +18,13 @@ package org.jboss.weld.junit5.auto;
 
 import org.jboss.weld.environment.se.Weld;
 import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldJunit5Extension;
+import org.jboss.weld.junit5.WeldJunitEnricher;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.platform.commons.support.AnnotationSupport;
 
 import static org.jboss.weld.junit5.ExtensionContextUtils.getExplicitInjectionInfoFromStore;
 
-import org.jboss.weld.junit5.WeldJunit5Extension;
-import org.jboss.weld.junit5.WeldJunitEnricher;
-
 /**
  * An alternative to {@link WeldJunit5Extension} allowing to fully leverage annotation based configuration approach.
  * When used, the extension will attempt to resolve all beans used in your test class and automatically adds them to
@@ -53,8 +52,6 @@ public class WeldJunit5AutoExtension extends WeldJunit5Extension {
 
         Class<?> testClass = testInstance.getClass();
 
-        weld.addAlternativeStereotype(OverrideBean.class);
-
         ClassScanning.scanForRequiredBeanClass(testClass, weld, getExplicitInjectionInfoFromStore(context));
 
         weld.addBeanClass(testClass);
diff --git a/pom.xml b/pom.xml
index d505d38..770182c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,11 +41,10 @@
       <maven.compiler.target>1.8</maven.compiler.target>
       <!-- Versions -->
       <version.junit4>4.12</version.junit4>
-      <version.junit.platform>1.1.0</version.junit.platform>
-      <version.junit.jupiter>5.1.0</version.junit.jupiter>
+      <version.junit.jupiter>5.4.1</version.junit.jupiter>
       <version.weld>2.4.8.Final</version.weld>
-      <version.weld3>3.0.5.Final</version.weld3>
-      <version.mockito>2.7.19</version.mockito>
+      <version.weld3>3.1.0.Final</version.weld3>
+      <version.mockito>2.25.1</version.mockito>
       <version.jboss-ejb-api>1.0.0.Final</version.jboss-ejb-api>
       <version.hibernate-jpa-api>1.0.0.Final</version.hibernate-jpa-api>
       <!-- SpotBugs properties -->
@@ -174,4 +173,25 @@
        </profile>
    </profiles>
 
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <!--Minimal version with Junit 5 engine support-->
+                    <version>2.22.1</version>
+                    <configuration>
+                        <systemProperties>
+                            <property>
+                                <name>java.util.logging.config.file</name>
+                                <value>${project.build.testOutputDirectory}/logging.properties</value>
+                            </property>
+                        </systemProperties>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
 </project>


Test patch:
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/alternative/AlternativeAsSoleBeanInSyntheticArchiveTest.java b/junit5/src/test/java/org/jboss/weld/junit5/alternative/AlternativeAsSoleBeanInSyntheticArchiveTest.java
new file mode 100644
index 0000000..c23c1f5
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/alternative/AlternativeAsSoleBeanInSyntheticArchiveTest.java
@@ -0,0 +1,39 @@
+package org.jboss.weld.junit5.alternative;
+
+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;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.Bean;
+import javax.inject.Inject;
+
+/**
+ * Tests that synthetic archive with only enabled alternative in it still works.
+ * This mimics problems in issue 64, but without the need for discovery
+ */
+@EnableWeld
+public class AlternativeAsSoleBeanInSyntheticArchiveTest {
+
+
+    @WeldSetup
+    public WeldInitiator weld = WeldInitiator.from(WeldInitiator.createWeld())
+            .addBeans(createSelectedAlternativeBean()).build();
+
+
+    static Bean<?> createSelectedAlternativeBean() {
+        return MockBean.builder().types(Fish.class).scope(ApplicationScoped.class).selectedAlternative(Fish.class)
+                .creating(new Fish(200)).build();
+    }
+
+    @Inject
+    Fish fish;
+
+    @Test
+    public void testAlternativeCanBeUsed() {
+        Assertions.assertTrue(fish.getNumberOfLegs() == 200);
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/alternative/Fish.java b/junit5/src/test/java/org/jboss/weld/junit5/alternative/Fish.java
new file mode 100644
index 0000000..6d06cc5
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/alternative/Fish.java
@@ -0,0 +1,14 @@
+package org.jboss.weld.junit5.alternative;
+
+public class Fish {
+
+    private int legs;
+
+    public Fish(int numberOfLegs) {
+        this.legs = numberOfLegs;
+    }
+
+    public int getNumberOfLegs() {
+        return legs;
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesInheritanceTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesInheritanceTest.java
index eaee76c..402aca7 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesInheritanceTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesInheritanceTest.java
@@ -1,8 +1,10 @@
 package org.jboss.weld.junit5.auto;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.jboss.weld.junit5.auto.beans.Engine;
+import org.jboss.weld.junit5.auto.beans.V6;
+import org.jboss.weld.junit5.auto.beans.V8;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
 
 import javax.enterprise.context.ContextNotActiveException;
 import javax.enterprise.context.ConversationScoped;
@@ -12,11 +14,9 @@ import javax.enterprise.inject.Produces;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.inject.Named;
 
-import org.jboss.weld.junit5.auto.beans.Engine;
-import org.jboss.weld.junit5.auto.beans.V6;
-import org.jboss.weld.junit5.auto.beans.V8;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 class ActivateScopesInheritanceTest extends BaseActivateScopesInheritanceTest {
 
@@ -27,8 +27,7 @@ class ActivateScopesInheritanceTest extends BaseActivateScopesInheritanceTest {
 
     @Produces
     @ConversationScoped
-    @OverrideBean
-    // V8 is annotated with @ApplicationScoped, this tells the container to use this ConversationScoped bean instead
+    @ExcludeBean // V6 is annotated with @ApplicationScoped, this tells the container to use producer instead
     V6 convoEngine = new V6();
 
     @Test
@@ -59,4 +58,4 @@ class ActivateScopesInheritanceTest extends BaseActivateScopesInheritanceTest {
 @ActivateScopes({ SessionScoped.class, RequestScoped.class })
 class BaseActivateScopesInheritanceTest {
     // empty, just a base class to let annotations be inherited
-}
\ No newline at end of file
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesTest.java
index 012c56e..6c72565 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ActivateScopesTest.java
@@ -32,8 +32,7 @@ class ActivateScopesTest {
 
   @Produces
   @ConversationScoped
-  @OverrideBean
-  // V8 is annotated with @ApplicationScoped, this tells the container to use this ConversationScoped bean instead
+  @ExcludeBean // V6 is annotated with @ApplicationScoped, this tells the container to use this producer instead
   V6 convoEngine = new V6();
 
   @Test
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesDepsTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesDepsTest.java
new file mode 100644
index 0000000..01ce801
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesDepsTest.java
@@ -0,0 +1,33 @@
+package org.jboss.weld.junit5.auto;
+
+import org.jboss.weld.junit5.basic.unsatisfied.FooDeps;
+import org.jboss.weld.junit5.basic.unsatisfied.SomeFooDeps;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.inject.Produces;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+@EnableAutoWeld
+@AddBeanClasses(SomeFooDeps.class)
+@ExcludeBeanClasses(FooDeps.class)
+class ExcludeBeanClassesDepsTest {
+
+    /**
+     * FooDeps injects the Baz bean which has an unsatisfied dependency "bar-value". Excluding FooDeps should ensure
+     * that its specific dependencies are not included via scanning and therefore don't need to be provided for
+     * testing.
+     */
+
+    @Produces
+    FooDeps fakeFooDeps = new FooDeps();
+
+    @Test
+    @DisplayName("Ensure @ExcludeBeanClasses excludes any specific dependencies of the excluded classes")
+    void test(FooDeps myFooDeps) {
+        assertNotNull(myFooDeps);
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesTest.java
new file mode 100644
index 0000000..1c2f660
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanClassesTest.java
@@ -0,0 +1,29 @@
+package org.jboss.weld.junit5.auto;
+
+import org.jboss.weld.junit5.basic.SomeFoo;
+import org.jboss.weld.junit5.basic.Foo;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.inject.Produces;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+@EnableAutoWeld
+@AddBeanClasses(SomeFoo.class)
+@ExcludeBeanClasses(Foo.class)
+class ExcludeBeanClassesTest {
+
+    @Produces
+    static Foo fakeFoo = new Foo("non-baz");
+
+    @Test
+    @DisplayName("Ensure @ExcludeBeanClasses excludes the specified classes")
+    void test(Foo myFoo) {
+        assertNotNull(myFoo);
+        assertEquals(myFoo.getBar(), "non-baz");
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanDepsTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanDepsTest.java
new file mode 100644
index 0000000..bc5660b
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanDepsTest.java
@@ -0,0 +1,33 @@
+package org.jboss.weld.junit5.auto;
+
+import org.jboss.weld.junit5.basic.unsatisfied.FooDeps;
+import org.jboss.weld.junit5.basic.unsatisfied.SomeFooDeps;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.inject.Produces;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+@EnableAutoWeld
+@AddBeanClasses(SomeFooDeps.class)
+class ExcludeBeanDepsTest {
+
+    /**
+     * FooDeps injects the Baz bean which has an unsatisfied dependency "bar-value". Excluding FooDeps should ensure
+     * that its specific dependencies are not included via scanning and therefore don't need to be provided for
+     * testing.
+     */
+
+    @Produces
+    @ExcludeBean
+    FooDeps fakeFooDeps = new FooDeps();
+
+    @Test
+    @DisplayName("Ensure @ExcludeBean excludes any specific dependencies of the excluded class")
+    void test(FooDeps myFooDeps) {
+        assertNotNull(myFooDeps);
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanHierarchyTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanHierarchyTest.java
new file mode 100644
index 0000000..c025c49
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanHierarchyTest.java
@@ -0,0 +1,30 @@
+package org.jboss.weld.junit5.auto;
+
+import org.jboss.weld.junit5.basic.Foo;
+import org.jboss.weld.junit5.basic.IFoo;
+import org.jboss.weld.junit5.basic.SomeIFoo;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.inject.Produces;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+@EnableAutoWeld
+@AddBeanClasses(SomeIFoo.class)
+class ExcludeBeanHierarchyTest {
+
+    @Produces
+    @ExcludeBean
+    IFoo fakeFoo = new Foo("non-baz");
+
+    @Test
+    @DisplayName("Ensure @ExcludeBean excludes all beans in implied hierarchy")
+    void test(IFoo myIFoo) {
+        assertNotNull(myIFoo);
+        assertEquals(myIFoo.getBar(), "non-baz");
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanTest.java
new file mode 100644
index 0000000..439a42b
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ExcludeBeanTest.java
@@ -0,0 +1,29 @@
+package org.jboss.weld.junit5.auto;
+
+import org.jboss.weld.junit5.basic.Foo;
+import org.jboss.weld.junit5.basic.SomeFoo;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import javax.enterprise.inject.Produces;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+
+@EnableAutoWeld
+@AddBeanClasses(SomeFoo.class)
+class ExcludeBeanTest {
+
+    @Produces
+    @ExcludeBean
+    Foo fakeFoo = new Foo("non-baz");
+
+    @Test
+    @DisplayName("Ensure @ExcludeBean excludes the implied bean class")
+    void test(Foo myFoo) {
+        assertNotNull(myFoo);
+        assertEquals(myFoo.getBar(), "non-baz");
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducesAlternativeTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducesAlternativeTest.java
deleted file mode 100644
index 6005e72..0000000
--- a/junit5/src/test/java/org/jboss/weld/junit5/auto/ProducesAlternativeTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.jboss.weld.junit5.auto;
-
-import org.jboss.weld.junit5.basic.Foo;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import javax.enterprise.inject.Produces;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-
-
-@EnableAutoWeld
-@AddBeanClasses(Foo.class)
-class ProducesAlternativeTest {
-
-  @Produces
-  @OverrideBean
-  Foo fakeFoo = new Foo("non-baz");
-
-  @Test
-  @DisplayName("Ensure @Produces/@OverrideBean overrides the @ApplicationScoped bean defined by the Foo class")
-  void test(Foo myFoo) {
-    assertNotNull(myFoo);
-    assertEquals(myFoo.getBar(), "non-baz");
-  }
-
-}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/DisabledMethodsTest.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/DisabledMethodsTest.java
new file mode 100644
index 0000000..d23dcd1
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/DisabledMethodsTest.java
@@ -0,0 +1,55 @@
+package org.jboss.weld.junit5.basic;
+
+import org.jboss.weld.environment.se.WeldContainer;
+import org.jboss.weld.junit5.WeldInitiator;
+import org.jboss.weld.junit5.WeldJunit5Extension;
+import org.jboss.weld.junit5.WeldSetup;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests that disabled methods do not trigger Weld container startup.
+ * For this test to work, @TestMethodOrder is required.
+ * Failure of this test might cause failures of other tests because of multiple active containers!
+ *
+ * @author Matej Novotny
+ */
+@ExtendWith(WeldJunit5Extension.class)
+@TestMethodOrder(value = MethodOrderer.Alphanumeric.class) // enforces ordering of methods, required!
+public class DisabledMethodsTest {
+
+    @WeldSetup
+    public WeldInitiator weld = WeldInitiator.of(Foo.class);
+
+    @BeforeEach
+    public void tryUseWeldInBeforeEach() {
+        assertTrue(weld.isRunning());
+        assertTrue(weld.select(Foo.class).isResolvable());
+    }
+
+    @AfterEach
+    public void tryUseWeldAfterEachMethod() {
+        assertTrue(weld.isRunning());
+        assertTrue(weld.select(Foo.class).isResolvable());
+    }
+
+    @Test
+    @Disabled
+    public void executedFirst() {
+        // dummy disabled test method
+    }
+
+    @Test
+    public void testRunningContainers() {
+        // we assert that there is only one container running at this point
+        assertEquals(1, WeldContainer.getRunningContainerIds().size());
+    }
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/Foo.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/Foo.java
index 7cbf44b..08fa73e 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/basic/Foo.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/Foo.java
@@ -22,7 +22,7 @@ import javax.enterprise.context.ApplicationScoped;
 
 
 @ApplicationScoped
-public class Foo {
+public class Foo implements IFoo {
 
     private String bar;
 
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/IFoo.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/IFoo.java
new file mode 100644
index 0000000..4f245a5
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/IFoo.java
@@ -0,0 +1,7 @@
+package org.jboss.weld.junit5.basic;
+
+public interface IFoo {
+
+    String getBar();
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeFoo.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeFoo.java
new file mode 100644
index 0000000..599f12e
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeFoo.java
@@ -0,0 +1,12 @@
+package org.jboss.weld.junit5.basic;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+@ApplicationScoped
+public class SomeFoo {
+
+    @Inject
+    private Foo foo;
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeIFoo.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeIFoo.java
new file mode 100644
index 0000000..d346d95
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/SomeIFoo.java
@@ -0,0 +1,12 @@
+package org.jboss.weld.junit5.basic;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+@ApplicationScoped
+public class SomeIFoo {
+
+    @Inject
+    private IFoo foo;
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/Baz.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/Baz.java
new file mode 100644
index 0000000..d7338e5
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/Baz.java
@@ -0,0 +1,21 @@
+package org.jboss.weld.junit5.basic.unsatisfied;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+@ApplicationScoped
+public class Baz {
+
+    @Inject
+    @Named("bar-value")
+    String bar;
+
+    public Baz() {
+    }
+
+    public String getBar() {
+        return bar;
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/FooDeps.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/FooDeps.java
new file mode 100644
index 0000000..7edaeea
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/FooDeps.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.junit5.basic.unsatisfied;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+
+@ApplicationScoped
+public class FooDeps {
+
+    @Inject
+    Baz baz;
+
+    public FooDeps() {}
+
+    public String getBar() {
+        return baz.getBar();
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/SomeFooDeps.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/SomeFooDeps.java
new file mode 100644
index 0000000..e3bfd5d
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/unsatisfied/SomeFooDeps.java
@@ -0,0 +1,12 @@
+package org.jboss.weld.junit5.basic.unsatisfied;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+@ApplicationScoped
+public class SomeFooDeps {
+
+    @Inject
+    private FooDeps fooDeps;
+
+}