diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc index 2af1c73b84e..8191b9cf99d 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc @@ -78,13 +78,14 @@ xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overrid Overrides and Spring AOP Proxies] for details. The `@MockitoSpyBean` annotation uses the `WRAP` -xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-strategy[strategy], -and the original instance is wrapped in a Mockito spy. This strategy requires that -exactly one candidate bean exists. In contrast to `@MockitoBean`, if the original bean -would have been wrapped in a Spring AOP proxy, that proxy is still created around the spy. -See +xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-strategy[strategy]: +an early instance of the original bean is captured and used to create a Mockito spy. +This strategy requires that exactly one candidate bean exists. In contrast to +`@MockitoBean`, if the original bean would have been wrapped in a Spring AOP proxy, that +proxy is still created — but it now wraps the spy instead of the original bean. See <> -for the consequences this has for stubbing and verification. +for a diagram and further details on the consequences this has for stubbing and +verification. [TIP] ==== diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc index f7297c18e2d..0c108878565 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/bean-overriding.adoc @@ -107,13 +107,49 @@ the override. original bean (`@Transactional`, `@Cacheable`, `@Retryable`, method security, and so on) is present. * Overrides that use the `WRAP` strategy (such as `@MockitoSpyBean`) capture an early - reference to the original bean and wrap it before the rest of the container's - post-processors — including the one responsible for creating AOP proxies — have run. - Consequently, if the original bean would have been proxied, that proxy is still - created, but with the override instance as its target rather than the original bean. - The bean that ends up in the `ApplicationContext`, and that is injected into - collaborating beans and test classes, is therefore a proxy wrapping the override - instance, not the bare override instance itself. + reference to the original bean and use it to create the override instance, before the + rest of the container's post-processors — including the one responsible for creating + AOP proxies — have run. Consequently, if the original bean would have been proxied, + that proxy is still created, but it now wraps the override instance instead of the + original bean. The bean that ends up in the `ApplicationContext`, and that is injected + into collaborating beans and test classes, is therefore the AOP proxy, with the + override instance as its target — not the bare override instance itself. + +The following diagrams illustrate the resulting shape of the bean for each strategy, from +the perspective of a caller invoking a method on the injected bean. + +With the `REPLACE` or `REPLACE_OR_CREATE` strategy, there is no AOP proxy at all: the +caller invokes the override instance directly. + +[source] +---- +caller + │ + ▼ +[ override instance ] +---- + +With the `WRAP` strategy, any AOP proxy that would normally have wrapped the original +bean is still created, but now wraps the override instance instead: + +[source] +---- +caller + │ + ▼ +[ AOP proxy ] (for example, retry, caching, or transaction advice) + │ + │ delegates to its target + ▼ +[ override instance ] (for example, a Mockito spy created by @MockitoSpyBean) +---- + +For a `WRAP`-based override such as `@MockitoSpyBean`, the "wrapping" performed by the +AOP proxy is unrelated to the manner in which the resulting Mockito spy itself "wraps" +the original bean instance it was created from. The proxy shown above determines which +object a caller actually invokes, whereas the spy's relationship to the original +instance only determines what happens when an unstubbed method is invoked on the spy: it +falls through to that instance's real behavior. This distinction has practical consequences when combining bean overrides with Mockito's stubbing and verification APIs. See