3
3
import io .dinject .core .BeanContextFactory ;
4
4
import io .dinject .core .Builder ;
5
5
import io .dinject .core .BuilderFactory ;
6
+ import io .dinject .core .EnrichBean ;
6
7
import io .dinject .core .SuppliedBean ;
7
8
import org .slf4j .Logger ;
8
9
import org .slf4j .LoggerFactory ;
16
17
import java .util .Map ;
17
18
import java .util .ServiceLoader ;
18
19
import java .util .Set ;
20
+ import java .util .function .Consumer ;
19
21
20
22
/**
21
23
* Boot and create a bean context with options for shutdown hook and supplying test doubles.
@@ -54,6 +56,8 @@ public class BootContext {
54
56
55
57
private final List <SuppliedBean > suppliedBeans = new ArrayList <>();
56
58
59
+ private final List <EnrichBean > enrichBeans = new ArrayList <>();
60
+
57
61
private final Set <String > includeModules = new LinkedHashSet <>();
58
62
59
63
private boolean ignoreMissingModuleDependencies ;
@@ -149,38 +153,41 @@ public BootContext withIgnoreMissingModuleDependencies() {
149
153
}
150
154
151
155
/**
152
- * Supply a bean to the context that will be used instead of any similar bean in the context.
156
+ * Supply a bean to the context that will be used instead of any
157
+ * similar bean in the context.
153
158
* <p>
154
- * This is typically expected to be used in tests and the bean supplied is typically a test double
155
- * or mock.
159
+ * This is typically expected to be used in tests and the bean
160
+ * supplied is typically a test double or mock.
156
161
* </p>
157
162
*
158
163
* <pre>{@code
159
164
*
160
- * @Test
161
- * public void someComponentTest() {
165
+ * Pump pump = mock(Pump.class);
166
+ * Grinder grinder = mock(Grinder.class);
162
167
*
163
- * MyRedisApi mockRedis = mock(MyRedisApi.class);
164
- * MyDbApi mockDatabase = mock(MyDbApi.class);
168
+ * try (BeanContext context = new BootContext()
169
+ * .withBeans(pump, grinder)
170
+ * .load()) {
165
171
*
166
- * try (BeanContext context = new BootContext()
167
- * .withBeans(mockRedis, mockDatabase)
168
- * .load()) {
172
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
173
+ * coffeeMaker.makeIt();
169
174
*
170
- * // built with test doubles injected ...
171
- * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
172
- * coffeeMaker.makeIt();
175
+ * Pump pump1 = context.getBean(Pump.class);
176
+ * Grinder grinder1 = context.getBean(Grinder.class);
173
177
*
174
- * assertThat(...
175
- * }
176
- * }
178
+ * assertThat(pump1).isSameAs(pump);
179
+ * assertThat(grinder1).isSameAs(grinder);
177
180
*
181
+ * verify(pump).pumpWater();
182
+ * verify(grinder).grindBeans();
183
+ * }
178
184
*
179
185
* }</pre>
180
186
*
181
187
* @param beans The bean used when injecting a dependency for this bean or the interface(s) it implements
182
188
* @return This BootContext
183
189
*/
190
+ @ SuppressWarnings ("unchecked" )
184
191
public BootContext withBeans (Object ... beans ) {
185
192
for (Object bean : beans ) {
186
193
suppliedBeans .add (new SuppliedBean (suppliedType (bean .getClass ()), bean ));
@@ -194,11 +201,148 @@ public BootContext withBeans(Object... beans) {
194
201
* This is typically a test double often created by Mockito or similar.
195
202
* </p>
196
203
*
204
+ * <pre>{@code
205
+ *
206
+ * try (BeanContext context = new BootContext()
207
+ * .withBean(Pump.class, mock)
208
+ * .load()) {
209
+ *
210
+ * Pump pump = context.getBean(Pump.class);
211
+ * assertThat(pump).isSameAs(mock);
212
+ *
213
+ * // act
214
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
215
+ * coffeeMaker.makeIt();
216
+ *
217
+ * verify(pump).pumpSteam();
218
+ * verify(pump).pumpWater();
219
+ * }
220
+ *
221
+ * }</pre>
222
+ *
197
223
* @param type The dependency injection type this bean is target for
198
224
* @param bean The supplied bean instance to use (typically a test mock)
199
225
*/
200
- public BootContext withBean (Class <?> type , Object bean ) {
201
- suppliedBeans .add (new SuppliedBean (type , bean ));
226
+ public <D > BootContext withBean (Class <D > type , D bean ) {
227
+ suppliedBeans .add (new SuppliedBean <>(type , bean ));
228
+ return this ;
229
+ }
230
+
231
+ /**
232
+ * Use a mockito mock when injecting this bean type.
233
+ *
234
+ * <pre>{@code
235
+ *
236
+ * try (BeanContext context = new BootContext()
237
+ * .withMock(Pump.class)
238
+ * .withMock(Grinder.class, grinder -> {
239
+ * // setup the mock
240
+ * when(grinder.grindBeans()).thenReturn("stub response");
241
+ * })
242
+ * .load()) {
243
+ *
244
+ *
245
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
246
+ * coffeeMaker.makeIt();
247
+ *
248
+ * // this is a mockito mock
249
+ * Grinder grinder = context.getBean(Grinder.class);
250
+ * verify(grinder).grindBeans();
251
+ * }
252
+ *
253
+ * }</pre>
254
+ */
255
+ public <D > BootContext withMock (Class <D > type ) {
256
+ return withMock (type , null );
257
+ }
258
+
259
+ /**
260
+ * Use a mockito mock when injecting this bean type additionally
261
+ * running setup on the mock instance.
262
+ *
263
+ * <pre>{@code
264
+ *
265
+ * try (BeanContext context = new BootContext()
266
+ * .withMock(Pump.class)
267
+ * .withMock(Grinder.class, grinder -> {
268
+ *
269
+ * // setup the mock
270
+ * when(grinder.grindBeans()).thenReturn("stub response");
271
+ * })
272
+ * .load()) {
273
+ *
274
+ *
275
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
276
+ * coffeeMaker.makeIt();
277
+ *
278
+ * // this is a mockito mock
279
+ * Grinder grinder = context.getBean(Grinder.class);
280
+ * verify(grinder).grindBeans();
281
+ * }
282
+ *
283
+ * }</pre>
284
+ */
285
+ public <D > BootContext withMock (Class <D > type , Consumer <D > consumer ) {
286
+ suppliedBeans .add (new SuppliedBean <>(type , null , consumer ));
287
+ return this ;
288
+ }
289
+
290
+ /**
291
+ * Use a mockito spy when injecting this bean type.
292
+ *
293
+ * <pre>{@code
294
+ *
295
+ * try (BeanContext context = new BootContext()
296
+ * .withSpy(Pump.class)
297
+ * .load()) {
298
+ *
299
+ * // setup spy here ...
300
+ * Pump pump = context.getBean(Pump.class);
301
+ * doNothing().when(pump).pumpSteam();
302
+ *
303
+ * // act
304
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
305
+ * coffeeMaker.makeIt();
306
+ *
307
+ * verify(pump).pumpWater();
308
+ * verify(pump).pumpSteam();
309
+ * }
310
+ *
311
+ * }</pre>
312
+ */
313
+ public <D > BootContext withSpy (Class <D > type ) {
314
+ return withSpy (type , null );
315
+ }
316
+
317
+ /**
318
+ * Use a mockito spy when injecting this bean type additionally
319
+ * running setup on the spy instance.
320
+ *
321
+ * <pre>{@code
322
+ *
323
+ * try (BeanContext context = new BootContext()
324
+ * .withSpy(Pump.class, pump -> {
325
+ * // setup the spy
326
+ * doNothing().when(pump).pumpWater();
327
+ * })
328
+ * .load()) {
329
+ *
330
+ * // or setup here ...
331
+ * Pump pump = context.getBean(Pump.class);
332
+ * doNothing().when(pump).pumpSteam();
333
+ *
334
+ * // act
335
+ * CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
336
+ * coffeeMaker.makeIt();
337
+ *
338
+ * verify(pump).pumpWater();
339
+ * verify(pump).pumpSteam();
340
+ * }
341
+ *
342
+ * }</pre>
343
+ */
344
+ public <D > BootContext withSpy (Class <D > type , Consumer <D > consumer ) {
345
+ enrichBeans .add (new EnrichBean <>(type , consumer ));
202
346
return this ;
203
347
}
204
348
@@ -214,7 +358,7 @@ public BeanContext load() {
214
358
Set <String > moduleNames = factoryOrder .orderFactories ();
215
359
log .debug ("building context with modules {}" , moduleNames );
216
360
217
- Builder rootBuilder = BuilderFactory .newRootBuilder (suppliedBeans );
361
+ Builder rootBuilder = BuilderFactory .newRootBuilder (suppliedBeans , enrichBeans );
218
362
219
363
for (BeanContextFactory factory : factoryOrder .factories ()) {
220
364
rootBuilder .addChild (factory .createContext (rootBuilder ));
0 commit comments