Repository: weld/weld-junit (java)

Issue:
Inject into test instance when using as @ClassRule.
If you are using weld-junit with a `@ClassRule` instead of a `@Rule` to not boot the container for every test individually, as far as I can see there is no way to inject into the test instance except by using `select` manually.

It would be nice if the `WeldInitiator` had an `inject` method too which can be used to inject into some instance, then it can be used in some `@Setup` method to inject into the test instance.

Maybe even better would be a `MethodRule` that gets the `WeldInitiator` as argument and injects into the test instance, something like this:
```java
@ClassRule
public static WeldInitiator weld = ...;
@Rule
public TestClassInjector = new TestClassInjector(weld);
```

Maybe even with a variation without argument that searches for `WeldInitiator` fields or methods and automatically uses them if only one is found.

Pull request:
Make it possible to inject into test instance when using @ClassRule (#105)
fixes #105 

Patch:
diff --git a/README.md b/README.md
index 567cc85..83e89ae 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ This project consists of three modules.
 Below is a list with links to detailed README of each extension:
 
 * JUnit 4 extension
-  * [JUnit 4 extension using `@Rule` mechanism](junit4/README.md)
+  * [JUnit 4 extension using `@Rule` and `@ClassRule` mechanism](junit4/README.md)
 * JUnit 5
   * [JUnit 5 extension using the `@ExtendWith` mechanism](junit5/README.md)
 * JUnit-common
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 a6a4208..f8a77d3 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
@@ -136,6 +136,26 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
         return new ToInject(instanceToInject);
     }
 
+    /**
+     * Injects the given non-contextual instance immediately. The returned {@link AutoCloseable} should be used
+     * to release the creational context once the injected beans are no longer needed.
+     *
+     * <p>Example:
+     * <pre>{@code
+     * try (AutoCloseable contextReleaser = injectNonContextual(this)) {
+     *     // do some things with the injected instances
+     * }
+     * }</pre>
+     *
+     * @param target the target to inject
+     * @return an {@code AutoCloseable} to release the creational context
+     */
+    public AutoCloseable injectNonContextual(Object target) {
+        ToInject toInject = new ToInject(target);
+        toInject.inject();
+        return toInject::release;
+    }
+
     @Override
     public Iterator<Object> iterator() {
         checkContainer();
@@ -382,7 +402,7 @@ public abstract class AbstractWeldInitiator implements Instance<Object>, Contain
          * </pre>
          *
          * <p>
-         * Injected {@link Dependent} bean instances are destroyed after the test execution. However, the licecycle of the non-contextual instance is not
+         * Injected {@link Dependent} bean instances are destroyed after the test execution. However, the lifecycle of the non-contextual instance is not
          * managed by the container and all injected references will be invalid after the test execution.
          * </p>
          *
diff --git a/junit4/README.md b/junit4/README.md
index 467ee81..94c792e 100644
--- a/junit4/README.md
+++ b/junit4/README.md
@@ -1,6 +1,6 @@
 # Weld JUnit 4 Extension
 
-Weld JUnit 4 extension uses the original JUnit extension system via `@Rule` annotation.
+Weld JUnit 4 extension uses the original JUnit extension system via `@Rule` or `@ClassRule` annotation.
 It requires JUnit 4.9+ and Java 8.
 
 ## Table of contents
@@ -27,7 +27,8 @@ It requires JUnit 4.9+ and Java 8.
 
 ## WeldInitiator
 
-`org.jboss.weld.junit4.WeldInitiator` is a `TestRule` (JUnit 4.9+) which allows to *start/stop* a Weld container per test method execution.
+`org.jboss.weld.junit4.WeldInitiator` is a `TestRule` (JUnit 4.9+) which allows to *start/stop*
+a Weld container per test method execution if used as `@Rule` or per test class if used as `@ClassRule`.
 The container is configured through a provided `org.jboss.weld.environment.se.Weld` instance.
 By default, the container is optimized for testing purposes, i.e. with automatic discovery and concurrent deployment disabled (see also `WeldInitiator.createWeld()`).
 However, it is possible to provide a customized `Weld` instance  - see also `WeldInitiator.of(Weld)` and `WeldInitiator.from(Weld)` methods.
@@ -100,7 +101,33 @@ class InjectTest {
     @Rule
     public WeldInitiator weld = WeldInitiator.from(Foo.class).inject(this).build();
 
-  // Gets injected by WeldInitiator when testFoo() is about to be run
+    // Gets injected by WeldInitiator when testFoo() is about to be run
+    @Inject
+    @MyQualifier
+    Foo foo;
+
+    @Test
+    public void testFoo() {
+        assertEquals(42, foo.getValue());
+    }
+}
+```
+
+When using the `WeldInitiator` as `@ClassRule` to only have one weld instance for the whole test class,
+`inject(this)` cannot be used, as no `this` reference is available yet and it cannot be done on the `WeldInitiator` itself.
+For this situation `WeldInitiator` provides the method `getTestClassInjectorRule()` that returns a `MethodRule`
+which can be used as a `@Rule` and injects into the test instance automatically:
+
+```java
+class InjectTest {
+
+    @ClassRule
+    public static WeldInitiator weld = WeldInitiator.from(Foo.class).build();
+
+    @Rule
+    public MethodRule testClassInjectorRule = weld.getTestClassInjectorRule();
+
+    // Gets injected by testClassInjector when testFoo() is about to be run
     @Inject
     @MyQualifier
     Foo foo;
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 5c0bb7f..a3933e4 100644
--- a/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
+++ b/junit4/src/main/java/org/jboss/weld/junit4/WeldInitiator.java
@@ -29,6 +29,9 @@ import javax.enterprise.inject.spi.InjectionPoint;
 
 import org.jboss.weld.environment.se.Weld;
 import org.jboss.weld.junit.AbstractWeldInitiator;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.MethodRule;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -213,6 +216,35 @@ public class WeldInitiator extends AbstractWeldInitiator implements TestRule {
         super(weld, instancesToInject, scopesToActivate, beans, resources, ejbFactory, persistenceUnitFactory, persistenceContextFactory);
     }
 
+    /**
+     * Returns a {@link MethodRule} that can be used as a {@link Rule} to inject into the test class instance.
+     * When using this Weld initiator as {@code Rule}, the same can be achieved by calling
+     * {@link AbstractBuilder#inject(Object) inject(this)} on the builder. But when using this Weld initiator as
+     * {@link ClassRule}, there is no {@code this} reference available. In that case this method can be used
+     * as {@code Rule} to do the test class injection part.
+     *
+     * <p><b>Example:</b>
+     * <pre>{@code
+     * }&#64;{@code ClassRule
+     * public static WeldInitiator weld = WeldInitiator.from(Foo.class).build();
+     *
+     * }&#64;{@code Rule
+     * public MethodRule testClassInjectorRule = weld.getTestClassInjectorRule();
+     * }</pre>
+     *
+     * @return the test class injector rule
+     */
+    public MethodRule getTestClassInjectorRule() {
+        return (base, method, target) -> new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try (AutoCloseable contextReleaser = injectNonContextual(target)) {
+                    base.evaluate();
+                }
+            }
+        };
+    }
+
     @Override
     public Statement apply(final Statement base, final Description description) {
         return new Statement() {


Test patch:
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectTest.java b/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectTest.java
index 55f99c3..53a2c05 100644
--- a/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectTest.java
+++ b/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectTest.java
@@ -27,8 +27,6 @@ import org.jboss.weld.junit4.WeldInitiator;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
 /**
@@ -37,21 +35,19 @@ import org.junit.runners.model.Statement;
  */
 public class InjectTest {
 
+    private final WeldInitiator weld = WeldInitiator
+            .from(Foo.class, MeatyStringObserver.class, IamDependent.class)
+            .inject(this)
+            .build();
+
     @Rule
-    public RuleChain chain = RuleChain.outerRule(new TestRule() {
+    public RuleChain chain = RuleChain.outerRule((base, description) -> new Statement() {
         @Override
-        public Statement apply(final Statement base, Description description) {
-            return new Statement() {
-
-                @Override
-                public void evaluate() throws Throwable {
-                    base.evaluate();
-                    assertTrue(IamDependent.DESTROYED.get());
-                }
-            };
+        public void evaluate() throws Throwable {
+            base.evaluate();
+            assertTrue(IamDependent.DESTROYED.get());
         }
-    }).around(WeldInitiator.from(Foo.class, MeatyStringObserver.class, IamDependent.class)
-            .inject(this).build());
+    }).around(weld);
 
     @Inject
     Foo foo;
@@ -73,4 +69,18 @@ public class InjectTest {
         iamDependent.bang();
     }
 
+    @Test
+    public void testManualNonContextualInjection() throws Exception {
+        final InjectTest sut = new InjectTest();
+        try (AutoCloseable contextReleaser = weld.injectNonContextual(sut)) {
+            MeatyStringObserver.MESSAGES.clear();
+            assertEquals("baz", sut.foo.getBar());
+            sut.event.fire("hello");
+            assertEquals(1, MeatyStringObserver.MESSAGES.size());
+            assertEquals("hello", MeatyStringObserver.MESSAGES.get(0));
+            sut.iamDependent.bang();
+        }
+        assertTrue(IamDependent.DESTROYED.get());
+    }
+
 }
diff --git a/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectWithClassRuleTest.java b/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectWithClassRuleTest.java
new file mode 100644
index 0000000..a457fb8
--- /dev/null
+++ b/junit4/src/test/java/org/jboss/weld/junit4/inject/InjectWithClassRuleTest.java
@@ -0,0 +1,74 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2020, 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.inject;
+
+import org.jboss.weld.junit4.Foo;
+import org.jboss.weld.junit4.WeldInitiator;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.MethodRule;
+import org.junit.rules.RuleChain;
+import org.junit.runners.model.Statement;
+
+import javax.enterprise.event.Event;
+import javax.inject.Inject;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ *
+ * @author Björn Kautler
+ */
+public class InjectWithClassRuleTest {
+    private static WeldInitiator weld = WeldInitiator.from(Foo.class, MeatyStringObserver.class, IamDependent.class)
+            .build();
+
+    @ClassRule
+    public static RuleChain chain = RuleChain.outerRule((base, description) -> new Statement() {
+        @Override
+        public void evaluate() throws Throwable {
+            base.evaluate();
+            assertTrue(IamDependent.DESTROYED.get());
+        }
+    }).around(weld);
+
+    @Rule
+    public MethodRule testClassInjectorRule = weld.getTestClassInjectorRule();
+
+    @Inject
+    Foo foo;
+
+    @Inject
+    @Meaty
+    Event<String> event;
+
+    @Inject
+    IamDependent iamDependent;
+
+    @Test
+    public void testFoo() {
+        MeatyStringObserver.MESSAGES.clear();
+        assertEquals("baz", foo.getBar());
+        event.fire("hello");
+        assertEquals(1, MeatyStringObserver.MESSAGES.size());
+        assertEquals("hello", MeatyStringObserver.MESSAGES.get(0));
+        iamDependent.bang();
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/basic/SimpleTest.java b/junit5/src/test/java/org/jboss/weld/junit5/basic/SimpleTest.java
index 6322dd7..e2b6b4d 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/basic/SimpleTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/basic/SimpleTest.java
@@ -26,6 +26,8 @@ import org.jboss.weld.junit5.ofpackage.Alpha;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
+import javax.inject.Inject;
+
 /**
  *
  * @author Matej Novotny
@@ -49,5 +51,16 @@ public class SimpleTest {
         assertFalse(weld.select(Alpha.class).isResolvable());
     }
 
+    @Test
+    public void testManualNonContextualInjection() throws Exception {
+        final NonContextual sut = new NonContextual();
+        try (AutoCloseable contextReleaser = weld.injectNonContextual(sut)) {
+            assertEquals("baz", sut.foo.getBar());
+        }
+    }
 
+    private static class NonContextual {
+        @Inject
+        private Foo foo;
+    }
 }