Introduce @EasyMockBean bean override example
This commit introduces example support for a custom @EasyMockBean annotation that allows tests to use EasyMock as the mocking framework for bean overrides in a test's ApplicationContext. The point of this exercise is to ensure that it is possible for third parties to introduce bean override support for mocking frameworks other than Mockito, and that they can do so with the APIs currently in place. Closes gh-33562
This commit is contained in:
parent
6c2cba5d8a
commit
e587753b1d
|
|
@ -115,6 +115,7 @@ dependencies {
|
|||
api("org.codehaus.jettison:jettison:1.5.4")
|
||||
api("org.crac:crac:1.4.0")
|
||||
api("org.dom4j:dom4j:2.1.4")
|
||||
api("org.easymock:easymock:5.4.0")
|
||||
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.7")
|
||||
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
|
||||
api("org.eclipse:yasson:2.0.4")
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ dependencies {
|
|||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
testImplementation("org.awaitility:awaitility")
|
||||
testImplementation("org.easymock:easymock")
|
||||
testImplementation("org.hibernate:hibernate-core-jakarta")
|
||||
testImplementation("org.hibernate:hibernate-validator")
|
||||
testImplementation("org.hsqldb:hsqldb")
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ class AotIntegrationTests extends AbstractAotTests {
|
|||
@Test
|
||||
void endToEndTestsForSelectedTestClasses() {
|
||||
List<Class<?>> testClasses = List.of(
|
||||
org.springframework.test.context.bean.override.easymock.EasyMockBeanIntegrationTests.class,
|
||||
org.springframework.test.context.bean.override.mockito.MockitoBeanForByNameLookupIntegrationTests.class,
|
||||
org.springframework.test.context.junit4.SpringJUnit4ClassRunnerAppCtxTests.class,
|
||||
org.springframework.test.context.junit4.ParameterizedDependencyInjectionTests.class
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.easymock.MockType;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.test.context.bean.override.BeanOverride;
|
||||
|
||||
/**
|
||||
* {@code @EasyMockBean} is a field-level annotation that can be used in a test
|
||||
* class to signal that a bean should be replaced with an {@link org.easymock.EasyMock
|
||||
* EasyMock} mock.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@BeanOverride(EasyMockBeanOverrideProcessor.class)
|
||||
public @interface EasyMockBean {
|
||||
|
||||
/**
|
||||
* Alias for {@link #name}.
|
||||
*/
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The name of the bean to mock.
|
||||
* <p>Defaults to an empty string to denote that the name of the annotated
|
||||
* field should be used as the bean name.
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* The {@link MockType} to use when creating the mock.
|
||||
* <p>Defaults to {@link MockType#STRICT}.
|
||||
*/
|
||||
MockType mockType() default MockType.STRICT;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import org.easymock.EasyMockSupport;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.bean.override.example.ExampleService;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.reset;
|
||||
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link EasyMockBean @EasyMockBean}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@TestExecutionListeners(listeners = EasyMockResetTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS)
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class EasyMockBeanIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
ApplicationContext ctx;
|
||||
|
||||
@EasyMockBean
|
||||
ExampleService service;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void test1() {
|
||||
assertThat(ctx.getBean("service", ExampleService.class))
|
||||
.satisfies(this::assertIsEasyMock)
|
||||
.isSameAs(service);
|
||||
|
||||
// Before mock setup
|
||||
assertThat(service.greeting()).isNull();
|
||||
reset(service);
|
||||
|
||||
// After mock setup
|
||||
expect(service.greeting()).andReturn("mocked");
|
||||
replay(service);
|
||||
assertThat(service.greeting()).isEqualTo("mocked");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void test2() {
|
||||
assertThat(ctx.getBean("service", ExampleService.class))
|
||||
.satisfies(this::assertIsEasyMock)
|
||||
.isSameAs(service);
|
||||
|
||||
// Before mock setup
|
||||
assertThat(service.greeting()).isNull();
|
||||
reset(service);
|
||||
|
||||
// After mock setup
|
||||
expect(service.greeting()).andReturn("mocked");
|
||||
replay(service);
|
||||
assertThat(service.greeting()).isEqualTo("mocked");
|
||||
}
|
||||
|
||||
|
||||
private void assertIsEasyMock(Object obj) {
|
||||
assertThat(EasyMockSupport.isAMock(obj)).as("is EasyMock mock").isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
ExampleService service() {
|
||||
return () -> "enigma";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import org.easymock.MockType;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.SingletonBeanRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
||||
|
||||
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION;
|
||||
|
||||
/**
|
||||
* {@link OverrideMetadata} that provides support for {@link EasyMockBean @EasyMockBean}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class EasyMockBeanOverrideMetadata extends OverrideMetadata {
|
||||
|
||||
private final MockType mockType;
|
||||
|
||||
|
||||
EasyMockBeanOverrideMetadata(Field field, Class<?> typeToOverride, @Nullable String beanName,
|
||||
MockType mockType) {
|
||||
|
||||
super(field, ResolvableType.forClass(typeToOverride), beanName, REPLACE_OR_CREATE_DEFINITION);
|
||||
this.mockType = mockType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition,
|
||||
@Nullable Object existingBeanInstance) {
|
||||
|
||||
Class<?> typeToMock = getBeanType().getRawClass();
|
||||
return EasyMock.mock(beanName, this.mockType, typeToMock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void track(Object mock, SingletonBeanRegistry singletonBeanRegistry) {
|
||||
getEasyMockBeans(singletonBeanRegistry).add(mock);
|
||||
}
|
||||
|
||||
private EasyMockBeans getEasyMockBeans(SingletonBeanRegistry singletonBeanRegistry) {
|
||||
String className = EasyMockBeans.class.getName();
|
||||
EasyMockBeans easyMockBeans = null;
|
||||
try {
|
||||
easyMockBeans = (EasyMockBeans) singletonBeanRegistry.getSingleton(className);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ignored) {
|
||||
}
|
||||
if (easyMockBeans == null) {
|
||||
easyMockBeans = new EasyMockBeans();
|
||||
singletonBeanRegistry.registerSingleton(className, easyMockBeans);
|
||||
}
|
||||
return easyMockBeans;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.springframework.test.context.bean.override.BeanOverrideProcessor;
|
||||
import org.springframework.test.context.bean.override.OverrideMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link BeanOverrideProcessor} that provides support for {@link EasyMockBean @EasyMockBean}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class EasyMockBeanOverrideProcessor implements BeanOverrideProcessor {
|
||||
|
||||
@Override
|
||||
public OverrideMetadata createMetadata(Annotation annotation, Class<?> testClass, Field field) {
|
||||
EasyMockBean easyMockBean = (EasyMockBean) annotation;
|
||||
String beanName = (StringUtils.hasText(easyMockBean.name()) ? easyMockBean.name() : field.getName());
|
||||
return new EasyMockBeanOverrideMetadata(field, field.getType(), beanName, easyMockBean.mockType());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class EasyMockBeans {
|
||||
|
||||
private final List<Object> beans = new ArrayList<>();
|
||||
|
||||
void add(Object bean) {
|
||||
this.beans.add(bean);
|
||||
}
|
||||
|
||||
void resetAll() {
|
||||
this.beans.forEach(EasyMock::reset);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.context.bean.override.easymock;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
|
||||
/**
|
||||
* {@code TestExecutionListener} that provides support for resetting mocks
|
||||
* created via {@link EasyMockBean @EasyMockBean}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.2
|
||||
*/
|
||||
class EasyMockResetTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
resetMocks(testContext.getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
resetMocks(testContext.getApplicationContext());
|
||||
}
|
||||
|
||||
private void resetMocks(ApplicationContext applicationContext) {
|
||||
if (applicationContext instanceof ConfigurableApplicationContext configurableContext) {
|
||||
resetMocks(configurableContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetMocks(ConfigurableApplicationContext applicationContext) {
|
||||
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
|
||||
beanFactory.getBean(EasyMockBeans.class).resetAll();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue