|
| 1 | +[[spring-testing-annotation-beanoverriding]] |
| 2 | += Bean Overriding in Tests |
| 3 | + |
| 4 | +Bean Overriding in Tests refers to the ability to override specific beans in the Context |
| 5 | +for a test class, by annotating one or more fields in said test class. |
| 6 | + |
| 7 | +NOTE: This is intended as a less risky alternative to the practice of registering a bean via |
| 8 | +`@Bean` with the `DefaultListableBeanFactory` `setAllowBeanDefinitionOverriding` set to |
| 9 | +`true`. |
| 10 | + |
| 11 | +The Spring Testing Framework provides two sets of annotations presented below. One relies |
| 12 | +purely on Spring, while the second set relies on the Mockito third party library. |
| 13 | + |
| 14 | +[[spring-testing-annotation-beanoverriding-testbean]] |
| 15 | +== `@TestBean` |
| 16 | + |
| 17 | +`@TestBean` is used on a test class field to override a specific bean with an instance |
| 18 | +provided by a conventionally named static method. |
| 19 | + |
| 20 | +By default, the bean name and the associated static method name are derived from the |
| 21 | +annotated field's name, but the annotation allows for specific values to be provided. |
| 22 | + |
| 23 | +The `@TestBean` annotation uses the `REPLACE_DEFINITION` |
| 24 | +xref:#spring-testing-annotation-beanoverriding-extending[strategy for test bean overriding]. |
| 25 | + |
| 26 | +The following example shows how to fully configure the `@TestBean` annotation, with |
| 27 | +explicit values equivalent to the default: |
| 28 | + |
| 29 | +[tabs] |
| 30 | +====== |
| 31 | +Java:: |
| 32 | ++ |
| 33 | +[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
| 34 | +---- |
| 35 | + class OverrideBeanTests { |
| 36 | + @TestBean(name = "service", methodName = "serviceTestOverride") // <1> |
| 37 | + private CustomService service; |
| 38 | +
|
| 39 | + // test case body... |
| 40 | +
|
| 41 | + private static CustomService serviceTestOverride() { // <2> |
| 42 | + return new MyFakeCustomService(); |
| 43 | + } |
| 44 | + } |
| 45 | +---- |
| 46 | +<1> Mark a field for bean overriding in this test class |
| 47 | +<2> The result of this static method will be used as the instance and injected into the field |
| 48 | +====== |
| 49 | + |
| 50 | + |
| 51 | +[[spring-testing-annotation-beanoverriding-mockitobean]] |
| 52 | +== `@MockitoBean` and `@MockitoSpyBean` |
| 53 | + |
| 54 | +`@MockitoBean` and `@MockitoSpyBean` are used on a test class field to override a bean |
| 55 | +with a mocking and spying instance, respectively. In the later case, the original bean |
| 56 | +definition is not replaced but instead an early instance is captured and wrapped by the |
| 57 | +spy. |
| 58 | + |
| 59 | +By default, the name of the bean to override is derived from the annotated field's name, |
| 60 | +but both annotations allows for a specific `name` to be provided. Each annotation also |
| 61 | +defines Mockito-specific attributes to fine-tune the mocking details. |
| 62 | + |
| 63 | +The `@MockitoBean` annotation uses the `CREATE_OR_REPLACE_DEFINITION` |
| 64 | +xref:#spring-testing-annotation-beanoverriding-extending[strategy for test bean overriding]. |
| 65 | + |
| 66 | +The `@MockitoSpyBean` annotation uses the `WRAP_EARLY_BEAN` |
| 67 | +xref:#spring-testing-annotation-beanoverriding-extending[strategy] and the original instance |
| 68 | +is wrapped in a Mockito spy. |
| 69 | + |
| 70 | +The following example shows how to configure the bean name for both `@MockitoBean` and |
| 71 | +`@MockitoSpyBean` annotations: |
| 72 | + |
| 73 | +[tabs] |
| 74 | +====== |
| 75 | +Java:: |
| 76 | ++ |
| 77 | +[source,java,indent=0,subs="verbatim,quotes",role="primary"] |
| 78 | +---- |
| 79 | + class OverrideBeanTests { |
| 80 | + @MockitoBean(name = "service1") // <1> |
| 81 | + private CustomService mockService; |
| 82 | +
|
| 83 | + @MockitoSpyBean(name = "service2") // <2> |
| 84 | + private CustomService spyService; // <3> |
| 85 | +
|
| 86 | + // test case body... |
| 87 | + } |
| 88 | +---- |
| 89 | +<1> Mark `mockService` as a Mockito mock override of bean `service1` in this test class |
| 90 | +<2> Mark `spyService` as a Mockito spy override of bean `service2` in this test class |
| 91 | +<3> Both fields will be injected with the Mockito values (the mock and the spy respectively) |
| 92 | +====== |
| 93 | + |
| 94 | + |
| 95 | +[[spring-testing-annotation-beanoverriding-extending]] |
| 96 | +== Extending bean override with a custom annotation |
| 97 | + |
| 98 | +The three annotations introduced above build upon the `@BeanOverride` meta-annotation |
| 99 | +and associated infrastructure, which allows to define custom bean overriding variants. |
| 100 | + |
| 101 | +In order to provide an extension, three classes are needed: |
| 102 | + - a concrete `BeanOverrideProcessor` `<P>` |
| 103 | + - a concrete `OverrideMetadata` created by said processor |
| 104 | + - an annotation meta-annotated with `@BeanOverride(P.class)` |
| 105 | + |
| 106 | +The Spring TestContext Framework includes infrastructure classes that support bean |
| 107 | +overriding: a `BeanPostProcessor`, a `TestExecutionListener` and a `ContextCustomizerFactory`. |
| 108 | +These are automatically registered via the Spring TestContext Framework `spring.factories` |
| 109 | +file. |
| 110 | + |
| 111 | +The test classes are parsed looking for any field meta-annotated with `@BeanOverride`, |
| 112 | +instantiating the relevant `BeanOverrideProcessor` in order to register an `OverrideMetadata`. |
| 113 | + |
| 114 | +Then the `BeanOverrideBeanPostProcessor` will use that information to alter the Context, |
| 115 | +registering and replacing bean definitions as influenced by each metadata |
| 116 | +`BeanOverrideStrategy`: |
| 117 | + |
| 118 | + - `REPLACE_DEFINITION`: the bean post-processor replaces the bean definition. |
| 119 | +If it is not present in the context, an exception is thrown. |
| 120 | + - `CREATE_OR_REPLACE_DEFINITION`: same as above but if the bean definition is not present |
| 121 | +in the context, one is created |
| 122 | + - `WRAP_EARLY_BEAN`: an original instance is obtained via |
| 123 | +`SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(Object, String)` and |
| 124 | +provided to the processor during `OverrideMetadata` creation. |
| 125 | + |
| 126 | +NOTE: The Bean Overriding infrastructure works best with singleton beans. It also doesn't |
| 127 | +include any bean resolution (unlike e.g. an `@Autowired`-annotated field). As such, the |
| 128 | +name of the bean to override MUST be somehow provided to or computed by the |
| 129 | +`BeanOverrideProcessor`. Typically, the end user provides the name as part of the custom |
| 130 | +annotation's attributes, or the annotated field's name. |
0 commit comments