From b9c2b7b257c0f36ee489fcc2cabda2c17cc19908 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Wed, 22 Apr 2020 14:35:50 -0700 Subject: [PATCH] Prevent early initialization in MockitoPostProcessor Fixes gh-20665 --- .../mock/mockito/MockitoPostProcessor.java | 6 +- .../mockito/MockitoPostProcessorTests.java | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java index 41b5715a5cc..3e413389c40 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -247,9 +247,9 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda } private Set getExistingBeans(ConfigurableListableBeanFactory beanFactory, ResolvableType type) { - Set beans = new LinkedHashSet<>(Arrays.asList(beanFactory.getBeanNamesForType(type))); + Set beans = new LinkedHashSet<>(Arrays.asList(beanFactory.getBeanNamesForType(type, true, false))); String typeName = type.resolve(Object.class).getName(); - for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) { + for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class, true, false)) { beanName = BeanFactoryUtils.transformedBeanName(beanName); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (typeName.equals(beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE))) { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java index 0490be8196c..bfac4629007 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,18 @@ package org.springframework.boot.test.mock.mockito; +import java.util.Map; + import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.FailingExampleService; @@ -29,6 +36,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.core.Ordered; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; @@ -39,6 +49,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * @author Phillip Webb * @author Andy Wilkinson * @author Andreas Neiser + * @author Madhura Bhave */ class MockitoPostProcessorTests { @@ -123,6 +134,16 @@ class MockitoPostProcessorTests { assertThat(Mockito.mockingDetails(context.getBean("exampleQualified", ExampleService.class)).isSpy()).isTrue(); } + @Test + void postProcessorShouldNotTriggerEarlyInitialization() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(FactoryBeanRegisteringPostProcessor.class); + MockitoPostProcessor.register(context); + context.register(TestBeanFactoryPostProcessor.class); + context.register(EagerInitBean.class); + context.refresh(); + } + @Configuration(proxyBeanMethods = false) @MockBean(SomeInterface.class) static class MockedFactoryBean { @@ -258,6 +279,14 @@ class MockitoPostProcessorTests { } + @Configuration(proxyBeanMethods = false) + static class EagerInitBean { + + @MockBean + private ExampleService service; + + } + static class TestFactoryBean implements FactoryBean { @Override @@ -275,6 +304,33 @@ class MockitoPostProcessorTests { return true; } + }; + + static class FactoryBeanRegisteringPostProcessor implements BeanFactoryPostProcessor, Ordered { + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + RootBeanDefinition beanDefinition = new RootBeanDefinition(TestFactoryBean.class); + ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition("test", beanDefinition); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + } + + static class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + @Override + @SuppressWarnings("unchecked") + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + Map cache = (Map) ReflectionTestUtils.getField(beanFactory, + "factoryBeanInstanceCache"); + Assert.isTrue(cache.isEmpty(), "Early initialization of factory bean triggered."); + } + } interface SomeInterface {