Skip to content

Commit a5e549b

Browse files
committed
#20 Refactor mock/spy handling with DBuilderExtn
Provides a cleaner separation for handling mocks (supplied beans) and spy (enhancement)
1 parent c289d19 commit a5e549b

File tree

7 files changed

+102
-65
lines changed

7 files changed

+102
-65
lines changed

src/main/java/io/dinject/BootContext.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import io.dinject.core.BeanContextFactory;
44
import io.dinject.core.Builder;
55
import io.dinject.core.BuilderFactory;
6-
import io.dinject.core.SpyConsumer;
6+
import io.dinject.core.EnrichBean;
77
import io.dinject.core.SuppliedBean;
88
import org.slf4j.Logger;
99
import org.slf4j.LoggerFactory;
@@ -56,7 +56,7 @@ public class BootContext {
5656

5757
private final List<SuppliedBean> suppliedBeans = new ArrayList<>();
5858

59-
private final List<SpyConsumer> spyConsumers = new ArrayList<>();
59+
private final List<EnrichBean> enrichBeans = new ArrayList<>();
6060

6161
private final Set<String> includeModules = new LinkedHashSet<>();
6262

@@ -227,15 +227,15 @@ public <D> BootContext withMock(Class<D> type, Consumer<D> consumer) {
227227
* Use a mockito spy when injecting this bean type.
228228
*/
229229
public <D> BootContext withSpy(Class<D> type) {
230-
spyConsumers.add(new SpyConsumer<>(type, null));
230+
enrichBeans.add(new EnrichBean<>(type, null));
231231
return this;
232232
}
233233

234234
/**
235235
* Use a mockito spy when injecting this bean type additionally running setup on the spy instance.
236236
*/
237237
public <D> BootContext withSpy(Class<D> type, Consumer<D> consumer) {
238-
spyConsumers.add(new SpyConsumer<>(type, consumer));
238+
enrichBeans.add(new EnrichBean<>(type, consumer));
239239
return this;
240240
}
241241

@@ -251,7 +251,7 @@ public BeanContext load() {
251251
Set<String> moduleNames = factoryOrder.orderFactories();
252252
log.debug("building context with modules {}", moduleNames);
253253

254-
Builder rootBuilder = BuilderFactory.newRootBuilder(suppliedBeans, spyConsumers);
254+
Builder rootBuilder = BuilderFactory.newRootBuilder(suppliedBeans, enrichBeans);
255255

256256
for (BeanContextFactory factory : factoryOrder.factories()) {
257257
rootBuilder.addChild(factory.createContext(rootBuilder));

src/main/java/io/dinject/core/Builder.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,12 @@ public interface Builder {
126126
BeanContext build();
127127

128128
/**
129-
* Return a spy enhanced bean for registration into the context.
129+
* Return a potentially enriched bean for registration into the context.
130+
* Typically for use with mockito spy.
130131
*
131132
* @param bean The bean with dependencies injected
132133
* @param types The types this bean registers for
133-
* @return Either the bean or the spy enhanced bean to register into the context.
134+
* @return Either the bean or the enriched bean to register into the context.
134135
*/
135-
Object spy(Object bean, Class<?>[] types);
136+
Object enrich(Object bean, Class<?>[] types);
136137
}

src/main/java/io/dinject/core/BuilderFactory.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ public class BuilderFactory {
1313
* Create the root level Builder.
1414
*
1515
* @param suppliedBeans The list of beans (typically test doubles) supplied when building the context.
16-
* @param spyConsumers The list of classes we want to have with mockito spy enhancement
16+
* @param enrichBeans The list of classes we want to have with mockito spy enhancement
1717
*/
18-
public static Builder newRootBuilder(List<SuppliedBean> suppliedBeans, List<SpyConsumer> spyConsumers) {
19-
return new DBuilder(suppliedBeans, spyConsumers);
18+
public static Builder newRootBuilder(List<SuppliedBean> suppliedBeans, List<EnrichBean> enrichBeans) {
19+
20+
if (suppliedBeans.isEmpty() && enrichBeans.isEmpty()) {
21+
// simple case, no mocks or spies
22+
return new DBuilder();
23+
}
24+
return new DBuilderExtn(suppliedBeans, enrichBeans);
2025
}
2126

2227
/**

src/main/java/io/dinject/core/DBuilder.java

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.slf4j.LoggerFactory;
77

88
import java.util.ArrayList;
9-
import java.util.HashMap;
109
import java.util.LinkedHashMap;
1110
import java.util.List;
1211
import java.util.Map;
@@ -30,12 +29,7 @@ class DBuilder implements Builder {
3029
/**
3130
* The beans created and added to the context during building.
3231
*/
33-
private final DBeanMap beanMap = new DBeanMap();
34-
35-
/**
36-
* Supplied beans (test doubles) given to the context prior to building.
37-
*/
38-
private final boolean hasSuppliedBeans;
32+
final DBeanMap beanMap = new DBeanMap();
3933

4034
/**
4135
* The context/module name.
@@ -54,14 +48,12 @@ class DBuilder implements Builder {
5448

5549
private final Map<String, BeanContext> children = new LinkedHashMap<>();
5650

57-
private final Map<Class<?>, SpyConsumer> spyMap;
58-
5951
/**
6052
* Debug of the current bean being wired - used in injection errors.
6153
*/
6254
private Class<?> injectTarget;
6355

64-
private Builder parent;
56+
Builder parent;
6557

6658
/**
6759
* Create a named context for non-root builders.
@@ -70,29 +62,15 @@ class DBuilder implements Builder {
7062
this.name = name;
7163
this.provides = provides;
7264
this.dependsOn = dependsOn;
73-
this.hasSuppliedBeans = false;
74-
this.spyMap = null;
7565
}
7666

7767
/**
78-
* Create for the root builder with supplied beans (test doubles).
68+
* Create for the root builder.
7969
*/
80-
DBuilder(List<SuppliedBean> suppliedBeans, List<SpyConsumer> spyConsumers) {
70+
DBuilder() {
8171
this.name = null;
8272
this.provides = null;
8373
this.dependsOn = null;
84-
this.hasSuppliedBeans = (suppliedBeans != null && !suppliedBeans.isEmpty());
85-
if (hasSuppliedBeans) {
86-
beanMap.add(suppliedBeans);
87-
}
88-
if (spyConsumers == null || spyConsumers.isEmpty()) {
89-
spyMap = null;
90-
} else {
91-
spyMap = new HashMap<>();
92-
for (SpyConsumer spy : spyConsumers) {
93-
spyMap.put(spy.getType(), spy);
94-
}
95-
}
9674
}
9775

9876
@Override
@@ -117,9 +95,6 @@ public void setParent(Builder parent) {
11795

11896
@Override
11997
public boolean isAddBeanFor(Class<?> addForType, Class<?> injectTarget) {
120-
if (hasSuppliedBeans) {
121-
return !beanMap.isSupplied(addForType.getName());
122-
}
12398
if (parent == null) {
12499
return true;
125100
}
@@ -177,10 +152,8 @@ public void addChild(BeanContext child) {
177152
@Override
178153
public void register(Object bean, String name, Class<?>... types) {
179154
if (parent != null) {
180-
// spy consumers only exist on top level builder
181-
bean = parent.spy(bean, types);
182-
} else {
183-
bean = spy(bean, types);
155+
// enrichment only exist on top level builder
156+
bean = parent.enrich(bean, types);
184157
}
185158
beanMap.register(bean, name, types);
186159
}
@@ -189,27 +162,11 @@ public void register(Object bean, String name, Class<?>... types) {
189162
* Return the bean to register potentially with spy enhancement.
190163
*/
191164
@Override
192-
public Object spy(Object bean, Class<?>[] types) {
193-
if (spyMap != null) {
194-
SpyConsumer spyConsumer = spyMap.get(typeOf(bean, types));
195-
if (spyConsumer != null) {
196-
// enrich/enhance the bean for spying
197-
return spyConsumer.spy(bean);
198-
}
199-
}
165+
public Object enrich(Object bean, Class<?>[] types) {
166+
// only enriched by DBuilderExtn
200167
return bean;
201168
}
202169

203-
/**
204-
* Return the type to lookup for spy.
205-
*/
206-
private Class<?> typeOf(Object bean, Class<?>... types) {
207-
if (types.length > 0) {
208-
return types[0];
209-
}
210-
return bean.getClass();
211-
}
212-
213170
@Override
214171
public void registerPrimary(Object bean, String name, Class<?>... types) {
215172
beanMap.registerPrimary(bean, name, types);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.dinject.core;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
/**
8+
* Extended builder that supports supplied beans (mocks) and enriching beans (spy).
9+
*/
10+
public class DBuilderExtn extends DBuilder {
11+
12+
private final Map<Class<?>, EnrichBean> enrichMap = new HashMap<>();
13+
14+
private final boolean hasSuppliedBeans;
15+
16+
DBuilderExtn(List<SuppliedBean> suppliedBeans, List<EnrichBean> enrichBeans) {
17+
super();
18+
this.hasSuppliedBeans = (suppliedBeans != null && !suppliedBeans.isEmpty());
19+
if (hasSuppliedBeans) {
20+
beanMap.add(suppliedBeans);
21+
}
22+
if (enrichBeans != null && !enrichBeans.isEmpty()) {
23+
for (EnrichBean spy : enrichBeans) {
24+
enrichMap.put(spy.getType(), spy);
25+
}
26+
}
27+
}
28+
29+
/**
30+
* Checking for supplied beans (mocks).
31+
*/
32+
@Override
33+
public boolean isAddBeanFor(Class<?> addForType, Class<?> injectTarget) {
34+
if (hasSuppliedBeans) {
35+
return !beanMap.isSupplied(addForType.getName());
36+
}
37+
return true;
38+
}
39+
40+
/**
41+
* Register beans with potential enhancement (spy).
42+
*/
43+
@Override
44+
public void register(Object bean, String name, Class<?>... types) {
45+
if (parent != null) {
46+
throw new IllegalStateException("no");
47+
}
48+
// this is the top level builder (parent always null)
49+
bean = enrich(bean, types);
50+
beanMap.register(bean, name, types);
51+
}
52+
53+
/**
54+
* Potentially enrich the bean prior to registering with context.
55+
*/
56+
@SuppressWarnings("unchecked")
57+
@Override
58+
public Object enrich(Object bean, Class<?>[] types) {
59+
EnrichBean enrich = enrichMap.get(typeOf(bean, types));
60+
return enrich != null ? enrich.enrich(bean) : bean;
61+
}
62+
63+
/**
64+
* Return the type to lookup for enrichment.
65+
*/
66+
private Class<?> typeOf(Object bean, Class<?>... types) {
67+
if (types.length > 0) {
68+
return types[0];
69+
}
70+
return bean.getClass();
71+
}
72+
}

src/main/java/io/dinject/core/SpyConsumer.java renamed to src/main/java/io/dinject/core/EnrichBean.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
/**
88
* Holds Spy setup consumers for dependency injection using Mockito Spy.
99
*/
10-
public class SpyConsumer<B> {
10+
public class EnrichBean<B> {
1111

1212
private final Class<B> type;
1313

1414
private final Consumer<B> consumer;
1515

16-
public SpyConsumer(Class<B> type, Consumer<B> consumer) {
16+
public EnrichBean(Class<B> type, Consumer<B> consumer) {
1717
this.type = type;
1818
this.consumer = consumer;
1919
}
@@ -28,8 +28,9 @@ public Class<B> getType() {
2828
/**
2929
* Return the spy enhanced bean instance to use.
3030
*/
31-
public B spy(B bean) {
31+
public B enrich(B bean) {
3232

33+
// should extract a SPI for this. Only enrichment is Mockito spy at this point.
3334
B spy = Mockito.spy(bean);
3435
if (consumer != null) {
3536
consumer.accept(spy);

src/main/java/io/dinject/core/SuppliedBean.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public Class<B> getType() {
4848
*/
4949
public B getBean() {
5050
if (bean == null) {
51+
// should extract a SPI for this
5152
bean = Mockito.mock(type);
5253
}
5354
if (consumer != null) {

0 commit comments

Comments
 (0)