Repository: avaje/avaje-inject
Pull request #220 (2022-05-26 09:20:36)

Feature/219 soft dependency


Patch:
diff --git a/inject-generator/pom.xml b/inject-generator/pom.xml
index f305a78b..7f35a38d 100644
--- a/inject-generator/pom.xml
+++ b/inject-generator/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>io.avaje</groupId>
     <artifactId>avaje-inject-parent</artifactId>
-    <version>8.4</version>
+    <version>8.5</version>
   </parent>
 
   <artifactId>avaje-inject-generator</artifactId>
@@ -16,7 +16,7 @@
     <dependency>
       <groupId>io.avaje</groupId>
       <artifactId>avaje-inject</artifactId>
-      <version>8.4</version>
+      <version>8.5</version>
     </dependency>
 
     <!-- test dependencies -->
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java b/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
index d185487c..5eb6bb76 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
@@ -6,7 +6,13 @@ final class Dependency {
   private final boolean softDependency;
 
   Dependency(String name) {
-    this(name, false);
+    if (name.startsWith("soft:")) {
+      this.softDependency = true;
+      this.name = name.substring(5);
+    } else {
+      this.softDependency = false;
+      this.name = name;
+    }
   }
 
   Dependency(String name, boolean softDependency) {
@@ -16,7 +22,7 @@ final class Dependency {
 
   @Override
   public String toString() {
-    return name;
+    return softDependency ? "soft:" + name : name;
   }
 
   String getName() {
@@ -31,4 +37,8 @@ final class Dependency {
   boolean isSoftDependency() {
     return softDependency;
   }
+
+  String dependsOn() {
+    return toString();
+  }
 }
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java b/inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java
index 727db70b..8e31f602 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/MetaData.java
@@ -159,7 +159,7 @@ class MetaData {
       appendProvides(sb, "provides", provides);
     }
     if (!dependsOn.isEmpty()) {
-      appendProvides(sb, "dependsOn", dependsOn.stream().map(Dependency::getName).collect(Collectors.toList()));
+      appendProvides(sb, "dependsOn", dependsOn.stream().map(Dependency::dependsOn).collect(Collectors.toList()));
     }
     sb.append(")").append(NEWLINE);
     sb.append("  protected void build_").append(getBuildName()).append("() {").append(NEWLINE);
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
index 8b4c808f..6692b157 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
@@ -98,7 +98,7 @@ class MethodReader {
       dependsOn.add(GenericType.trimWildcard(param.paramType));
     }
     metaData.setDependsOn(dependsOn);
-    metaData.setProvides(new ArrayList<>());
+    metaData.setProvides(typeReader == null ? Collections.emptyList() : typeReader.getProvides());
     return metaData;
   }
 
diff --git a/inject/pom.xml b/inject/pom.xml
index 7510898a..5a65e42a 100644
--- a/inject/pom.xml
+++ b/inject/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>io.avaje</groupId>
     <artifactId>avaje-inject-parent</artifactId>
-    <version>8.4</version>
+    <version>8.5</version>
   </parent>
 
   <artifactId>avaje-inject</artifactId>
@@ -28,7 +28,7 @@
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
-      <version>4.3.1</version>
+      <version>4.5.1</version>
       <optional>true</optional>
     </dependency>
 
@@ -42,9 +42,9 @@
     </dependency>
 
     <dependency>
-      <groupId>org.avaje.composite</groupId>
+      <groupId>org.avaje</groupId>
       <artifactId>logback</artifactId>
-      <version>1.1</version>
+      <version>1.0</version>
       <scope>test</scope>
     </dependency>
 
diff --git a/inject/src/main/java/io/avaje/inject/BeanScope.java b/inject/src/main/java/io/avaje/inject/BeanScope.java
index 41a3684c..361070a8 100644
--- a/inject/src/main/java/io/avaje/inject/BeanScope.java
+++ b/inject/src/main/java/io/avaje/inject/BeanScope.java
@@ -226,6 +226,16 @@ public interface BeanScope extends AutoCloseable {
    */
   List<BeanEntry> all();
 
+  /**
+   * Return true if the bean scope contains the given type.
+   */
+  boolean contains(Type type);
+
+  /**
+   * Return true if the bean scope contains the given type.
+   */
+  boolean contains(String type);
+
   /**
    * Close the scope firing any <code>@PreDestroy</code> lifecycle methods.
    */
diff --git a/inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java b/inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java
index d2c4fcb9..d9c450df 100644
--- a/inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java
+++ b/inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java
@@ -137,7 +137,7 @@ class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
   @Override
   public BeanScope build() {
     // sort factories by dependsOn
-    FactoryOrder factoryOrder = new FactoryOrder(includeModules, !suppliedBeans.isEmpty());
+    FactoryOrder factoryOrder = new FactoryOrder(parent, includeModules, !suppliedBeans.isEmpty());
     if (factoryOrder.isEmpty()) {
       ServiceLoader.load(Module.class).forEach(factoryOrder::add);
     }
@@ -176,6 +176,7 @@ class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
    */
   static class FactoryOrder {
 
+    private final BeanScope parent;
     private final boolean suppliedBeans;
     private final Set<String> moduleNames = new LinkedHashSet<>();
     private final List<Module> factories = new ArrayList<>();
@@ -184,7 +185,8 @@ class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
 
     private final Map<String, FactoryList> providesMap = new HashMap<>();
 
-    FactoryOrder(Set<Module> includeModules, boolean suppliedBeans) {
+    FactoryOrder(BeanScope parent, Set<Module> includeModules, boolean suppliedBeans) {
+      this.parent = parent;
       this.factories.addAll(includeModules);
       this.suppliedBeans = suppliedBeans;
       for (Module includeModule : includeModules) {
@@ -274,12 +276,18 @@ class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
           }
         }
         sb.append(" - none of the loaded modules ").append(moduleNames).append(" have this in their @InjectModule( provides = ... ). ");
+        if (parent != null) {
+          sb.append("The parent BeanScope " + parent + " also does not provide this dependency. ");
+        }
         sb.append("Either @InjectModule requires/provides are not aligned? or add external dependencies via BeanScopeBuilder.bean()?");
         throw new IllegalStateException(sb.toString());
       }
     }
 
     private boolean notProvided(String dependency) {
+      if (parent != null && parent.contains(dependency)) {
+        return false;
+      }
       FactoryList factories = providesMap.get(dependency);
       return (factories == null || !factories.allPushed());
     }
diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java
index cf7101e3..1a49cab6 100644
--- a/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java
+++ b/inject/src/main/java/io/avaje/inject/spi/DBeanMap.java
@@ -84,6 +84,14 @@ class DBeanMap {
     return entry.getStrict(KeyUtil.lower(name));
   }
 
+  boolean contains(String type) {
+    return beans.containsKey(type);
+  }
+
+  boolean contains(Type type) {
+    return beans.containsKey(type.getTypeName());
+  }
+
   @SuppressWarnings("unchecked")
   <T> T get(Type type, String name) {
     DContextEntry entry = beans.get(type.getTypeName());
diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
index cf2e5e18..6904e9c7 100644
--- a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
+++ b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
@@ -59,6 +59,16 @@ class DBeanScope implements BeanScope {
     beans.addAll(map);
   }
 
+  @Override
+  public boolean contains(String type) {
+    return beans.contains(type);
+  }
+
+  @Override
+  public boolean contains(Type type) {
+    return beans.contains(type);
+  }
+
   @Override
   public <T> T get(Class<T> type) {
     return getByType(type, null);
diff --git a/pom.xml b/pom.xml
index 5eb7df0b..74cb7458 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
 
   <groupId>io.avaje</groupId>
   <artifactId>avaje-inject-parent</artifactId>
-  <version>8.4</version>
+  <version>8.5</version>
   <packaging>pom</packaging>
   <name>avaje inject parent</name>
   <description>parent pom for avaje inject library</description>


Test patch:
diff --git a/blackbox-test-inject/pom.xml b/blackbox-test-inject/pom.xml
index 66373e4c..2a2f0e6a 100644
--- a/blackbox-test-inject/pom.xml
+++ b/blackbox-test-inject/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <artifactId>avaje-inject-parent</artifactId>
     <groupId>io.avaje</groupId>
-    <version>8.4</version>
+    <version>8.5</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/DependencyTest.java b/inject-generator/src/test/java/io/avaje/inject/generator/DependencyTest.java
new file mode 100644
index 00000000..562d6dcf
--- /dev/null
+++ b/inject-generator/src/test/java/io/avaje/inject/generator/DependencyTest.java
@@ -0,0 +1,32 @@
+package io.avaje.inject.generator;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class DependencyTest {
+
+  @Test
+  void dependsOn_soft() {
+    Dependency dependency = new Dependency("org.foo.Bar", true);
+
+    assertThat(dependency.isSoftDependency()).isTrue();
+    assertThat(dependency.getName()).isEqualTo("org.foo.Bar");
+    assertThat(dependency.dependsOn()).isEqualTo("soft:org.foo.Bar");
+
+    Dependency dependencyFromMeta = new Dependency("soft:org.foo.Bar");
+    assertThat(dependencyFromMeta.getName()).isEqualTo("org.foo.Bar");
+    assertThat(dependencyFromMeta.isSoftDependency()).isTrue();
+    assertThat(dependencyFromMeta.getName()).isEqualTo(dependency.getName());
+    assertThat(dependencyFromMeta.dependsOn()).isEqualTo(dependency.dependsOn());
+  }
+
+  @Test
+  void dependsOn_notSoft() {
+    Dependency dependency = new Dependency("org.foo.Bar");
+
+    assertThat(dependency.dependsOn()).isEqualTo("org.foo.Bar");
+    assertThat(dependency.isSoftDependency()).isFalse();
+    assertThat(dependency.getName()).isEqualTo("org.foo.Bar");
+  }
+}
diff --git a/inject-test/pom.xml b/inject-test/pom.xml
index 5912661f..480c8448 100644
--- a/inject-test/pom.xml
+++ b/inject-test/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>io.avaje</groupId>
     <artifactId>avaje-inject-parent</artifactId>
-    <version>8.4</version>
+    <version>8.5</version>
   </parent>
 
   <artifactId>avaje-inject-test</artifactId>
@@ -124,7 +124,7 @@
             <path>
               <groupId>io.avaje</groupId>
               <artifactId>avaje-inject-generator</artifactId>
-              <version>8.4</version>
+              <version>8.5</version>
             </path>
           </annotationProcessorPaths>
         </configuration>
diff --git a/inject-test/src/main/java/io/avaje/inject/test/MetaInfo.java b/inject-test/src/main/java/io/avaje/inject/test/MetaInfo.java
index c3d3470c..3656b089 100644
--- a/inject-test/src/main/java/io/avaje/inject/test/MetaInfo.java
+++ b/inject-test/src/main/java/io/avaje/inject/test/MetaInfo.java
@@ -72,7 +72,9 @@ class MetaInfo {
     @Override
     public void close() {
       beanScope.close();
-      pluginScope.close();
+      if (pluginScope != null) {
+        pluginScope.close();
+      }
     }
   }
 }
diff --git a/inject-test/src/test/java/org/example/factory/MyBasicFactory.java b/inject-test/src/test/java/org/example/factory/MyBasicFactory.java
new file mode 100644
index 00000000..2304d78b
--- /dev/null
+++ b/inject-test/src/test/java/org/example/factory/MyBasicFactory.java
@@ -0,0 +1,34 @@
+package org.example.factory;
+
+import io.avaje.inject.Bean;
+import io.avaje.inject.Factory;
+
+@Factory
+public class MyBasicFactory {
+
+  @Bean(destroyMethod = "close")
+  OutIFace create() {
+    return new OutImpl();
+  }
+
+  static class OutImpl implements OutIFace {
+
+    @Override
+    public void doIt() {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String source() {
+      return null;
+    }
+  }
+}
+
+
+
diff --git a/inject-test/src/test/java/org/example/factory/MyUserOfOut.java b/inject-test/src/test/java/org/example/factory/MyUserOfOut.java
new file mode 100644
index 00000000..5827eb44
--- /dev/null
+++ b/inject-test/src/test/java/org/example/factory/MyUserOfOut.java
@@ -0,0 +1,13 @@
+package org.example.factory;
+
+import io.avaje.inject.Component;
+
+@Component
+public class MyUserOfOut {
+
+  final OutSource source;
+
+  public MyUserOfOut(OutSource source) {
+    this.source = source;
+  }
+}
diff --git a/inject-test/src/test/java/org/example/factory/MyUserOfOutTest.java b/inject-test/src/test/java/org/example/factory/MyUserOfOutTest.java
new file mode 100644
index 00000000..f5e52fee
--- /dev/null
+++ b/inject-test/src/test/java/org/example/factory/MyUserOfOutTest.java
@@ -0,0 +1,21 @@
+package org.example.factory;
+
+import io.avaje.inject.BeanScope;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class MyUserOfOutTest {
+
+  @Test
+  void factoryBeanWiredByInheritedInterface() {
+    try (BeanScope beanScope = BeanScope.builder().build()) {
+
+      MyUserOfOut myUserOfOut = beanScope.get(MyUserOfOut.class);
+      assertThat(myUserOfOut).isNotNull();
+
+      OutIFace outIFace = beanScope.get(OutIFace.class);
+      assertThat(outIFace).isSameAs(myUserOfOut.source);
+    }
+  }
+}
diff --git a/inject-test/src/test/java/org/example/factory/OutIFace.java b/inject-test/src/test/java/org/example/factory/OutIFace.java
new file mode 100644
index 00000000..091bac2c
--- /dev/null
+++ b/inject-test/src/test/java/org/example/factory/OutIFace.java
@@ -0,0 +1,8 @@
+package org.example.factory;
+
+public interface OutIFace extends OutSource {
+
+  void doIt();
+
+  void close();
+}
diff --git a/inject-test/src/test/java/org/example/factory/OutSource.java b/inject-test/src/test/java/org/example/factory/OutSource.java
new file mode 100644
index 00000000..9235d605
--- /dev/null
+++ b/inject-test/src/test/java/org/example/factory/OutSource.java
@@ -0,0 +1,6 @@
+package org.example.factory;
+
+public interface OutSource {
+
+  String source();
+}
diff --git a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
index c1abbd61..42800045 100644
--- a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
+++ b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
@@ -1,21 +1,42 @@
 package io.avaje.inject;
 
-import io.avaje.inject.spi.Module;
 import io.avaje.inject.spi.Builder;
+import io.avaje.inject.spi.Module;
 import org.junit.jupiter.api.Test;
 
-import java.util.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-public class BeanScopeBuilderTest {
+class BeanScopeBuilderTest {
+
+  @Test
+  void depends_providedByParent() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(new TDBeanScope(MyFeature.class), Collections.emptySet(), false);
+    factoryOrder.add(bc("1", null, of(MyFeature.class)));
+    factoryOrder.orderFactories();
+
+    assertThat(names(factoryOrder.factories())).containsExactly("1");
+  }
 
   @Test
-  public void noDepends() {
+  void depends_notProvidedByParent_expect_IllegalStateException() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(new TDBeanScope(FeatureA.class), Collections.emptySet(), false);
+    factoryOrder.add(bc("1", null, of(MyFeature.class)));
+    assertThatThrownBy(factoryOrder::orderFactories)
+      .isInstanceOf(IllegalStateException.class)
+      .hasMessageContaining("Module [io.avaje.inject.BeanScopeBuilderTest$TDModule] has unsatisfied requires [io.avaje.inject.BeanScopeBuilderTest$MyFeature]");
+  }
 
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  @Test
+  void noDepends() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("1", null, null));
     factoryOrder.add(bc("2", null, null));
     factoryOrder.add(bc("3", null, null));
@@ -25,9 +46,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void name_depends() {
-
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void name_depends() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("two", null, of(Mod3.class)));
     factoryOrder.add(bc("one", null, null));
     factoryOrder.orderFactories();
@@ -36,9 +56,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void name_depends4() {
-
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void name_depends4() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("1", null, of(Mod3.class)));
     factoryOrder.add(bc("2", null, of(Mod4.class)));
     factoryOrder.add(bc("3", of(Mod3.class), of(Mod4.class)));
@@ -50,9 +69,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void nameFeature_depends() {
-
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void nameFeature_depends() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("1", of(FeatureA.class), of(Mod3.class)));
     factoryOrder.add(bc("2", null, of(Mod4.class, FeatureA.class)));
     factoryOrder.add(bc("3", of(Mod3.class), of(Mod4.class)));
@@ -64,9 +82,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void feature_depends() {
-
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void feature_depends() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("two", null, of(MyFeature.class)));
     factoryOrder.add(bc("one", of(MyFeature.class), null));
     factoryOrder.orderFactories();
@@ -75,9 +92,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void feature_depends2() {
-
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void feature_depends2() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("two", null, of(MyFeature.class)));
     factoryOrder.add(bc("one", of(MyFeature.class), null));
     factoryOrder.add(bc("three", of(MyFeature.class), null));
@@ -87,8 +103,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void name_requiresPackage() {
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void name_requiresPackage() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("1", null, new Class[0], of(Mod3.class)));
     factoryOrder.add(bc("2", null, new Class[0], of(Mod4.class)));
     factoryOrder.add(bc("3", of(Mod3.class), new Class[0], of(Mod4.class)));
@@ -100,8 +116,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void name_requiresPackage_mixed() {
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), true);
+  void name_requiresPackage_mixed() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), true);
     factoryOrder.add(bc("1", null, new Class[0], of(Mod3.class)));
     factoryOrder.add(bc("2", null, of(Mod4.class), new Class[0]));
     factoryOrder.add(bc("3", of(Mod3.class), new Class[0], of(Mod4.class)));
@@ -113,8 +129,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void missingRequiresPackage_expect_unsatisfiedRequiresPackages() {
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), false);
+  void missingRequiresPackage_expect_unsatisfiedRequiresPackages() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), false);
     factoryOrder.add(bc("1", null, new Class[0], of(Mod3.class)));
     factoryOrder.add(bc("2", null, of(Mod4.class), new Class[0]));
     factoryOrder.add(bc("4", of(Mod4.class), new Class[0]));
@@ -125,8 +141,8 @@ public class BeanScopeBuilderTest {
   }
 
   @Test
-  public void missingRequires_expect_unsatisfiedRequires() {
-    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(Collections.emptySet(), false);
+  void missingRequires_expect_unsatisfiedRequires() {
+    DBeanScopeBuilder.FactoryOrder factoryOrder = new DBeanScopeBuilder.FactoryOrder(null, Collections.emptySet(), false);
     factoryOrder.add(bc("1", null, of(Mod3.class), new Class[0]));
     factoryOrder.add(bc("2", null, of(Mod4.class), new Class[0]));
     factoryOrder.add(bc("4", of(Mod4.class), new Class[0]));
@@ -142,22 +158,22 @@ public class BeanScopeBuilderTest {
       .collect(Collectors.toList());
   }
 
-  private TDBeanScope bc(String name, Class<?>[] provides, Class<?>[] requires) {
+  private TDModule bc(String name, Class<?>[] provides, Class<?>[] requires) {
     return bc(name, provides, requires, new Class[0]);
   }
 
-  private TDBeanScope bc(String name, Class<?>[] provides, Class<?>[] requires, Class<?>[] requiresPkg) {
-    return new TDBeanScope(name, provides, requires, requiresPkg);
+  private TDModule bc(String name, Class<?>[] provides, Class<?>[] requires, Class<?>[] requiresPkg) {
+    return new TDModule(name, provides, requires, requiresPkg);
   }
 
-  private static class TDBeanScope implements Module {
+  private static class TDModule implements Module {
 
     final String name;
     final Class<?>[] provides;
     final Class<?>[] requires;
     final Class<?>[] requiresPackages;
 
-    private TDBeanScope(String name, Class<?>[] provides, Class<?>[] requires, Class<?>[] requiresPackages) {
+    private TDModule(String name, Class<?>[] provides, Class<?>[] requires, Class<?>[] requiresPackages) {
       this.name = name;
       this.provides = provides;
       this.requires = requires;
@@ -207,4 +223,78 @@ public class BeanScopeBuilderTest {
   }
   class Mod4 {
   }
+
+  static class TDBeanScope implements BeanScope {
+
+    final String containsType;
+
+    TDBeanScope(Class<?> containsType) {
+      this.containsType = containsType.getTypeName();
+    }
+
+    @Override
+    public boolean contains(String type) {
+      return containsType.equals(type);
+    }
+
+    @Override
+    public <T> T get(Class<T> type) {
+      return null;
+    }
+
+    @Override
+    public <T> T get(Class<T> type, String name) {
+      return null;
+    }
+
+    @Override
+    public <T> T get(Type type, String name) {
+      return null;
+    }
+
+    @Override
+    public <T> Optional<T> getOptional(Class<T> type) {
+      return Optional.empty();
+    }
+
+    @Override
+    public <T> Optional<T> getOptional(Type type, String name) {
+      return Optional.empty();
+    }
+
+    @Override
+    public List<Object> listByAnnotation(Class<?> annotation) {
+      return null;
+    }
+
+    @Override
+    public <T> List<T> list(Class<T> interfaceType) {
+      return null;
+    }
+
+    @Override
+    public <T> List<T> listByPriority(Class<T> interfaceType) {
+      return null;
+    }
+
+    @Override
+    public <T> List<T> listByPriority(Class<T> interfaceType, Class<? extends Annotation> priority) {
+      return null;
+    }
+
+    @Override
+    public List<BeanEntry> all() {
+      return null;
+    }
+
+    @Override
+    public boolean contains(Type type) {
+      return false;
+    }
+
+    @Override
+    public void close() {
+
+    }
+  }
 }