我想我的一些豆子知道一些关于测试的东西。一些东西。可能是测试类名称或其中的一些方法。
例如,假设我的测试类有一个方法
public String getTestName() {
return getClass().getSimpleName();
}
此方法返回测试名称,可以覆盖。
是否可以将此名称注入Spring上下文的某些bean中,以便在测试期间使用?
例如,使用自动装配功能:
@Autowired
public String testName;
不仅在测试类中,而且在其他bean中也是如此。
更新
以下是两次(失败的)尝试实施注入testInstance
。可能有一些方便的方法吗?
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestClassAwareTry._Config.class)
@TestExecutionListeners(value = { TestClassAwareTry._Listener.class },
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class TestClassAwareTry {
/**
* Interface to tag beans, who want to know if they are in test
*/
public interface TestInstanceAware {
void setTestInstance(Object value);
}
/**
* Sample bean, which would like to know if it is in test
*/
public static class MyBean implements TestInstanceAware {
private Object testInstance;
{
System.out.println("MyBean constructed");
}
public void setTestInstance(Object value) {
this.testInstance = value;
System.out.println("testInstance set");
}
public Object getTestInstance() {
return testInstance;
}
}
/**
* Attempt to inject testInstance with a bean, implementing {@link BeanPostProcessor}
*/
public static class TestInstanceInjector implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if( bean instanceof TestInstanceAware ) {
TestInstanceAware aware = (TestInstanceAware) bean;
// we don't have access to test instance here
// otherwise I would write
//Object testInstance = getTestInstance();
//aware.setTestInstance(testInstance);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
/**
* Attempt to inject testInstance with test execution listener
*/
public static class _Listener extends AbstractTestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
Object testInstance = testContext.getTestInstance();
ApplicationContext context = testContext.getApplicationContext();
// we don't have setBean() method
// I would write if I have
// context.setBean("testInstance", context);
}
}
/**
* Java-based configuration
*/
@Configuration
public class _Config {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean
public TestInstanceInjector testInstanceInjector() {
return new TestInstanceInjector();
// I would acquire test instance here and pass it to constructor, if I can
}
}
@Autowired
public MyBean myBean;
@Test
public void testInjected() {
assertSame( this, myBean.getTestInstance());
}
}
答案 0 :(得分:1)
我能够做到这一点的唯一方法是延迟创建主题,直到你进入测试方法并将bean放在原型范围内。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { LiveConfig.class, DevConfig.class})
@ActiveProfiles("Dev")
public class MeTest {
@Autowired
public ApplicationContext context;
@Autowired
DevConfig devConfig;
@Rule
public TestName nameRule = new TestName();
@Before
public void setName() {
devConfig.setSettings(nameRule.getMethodName());
}
@Test
public void test() {
Bean subject = context.getBean(Bean.class);
System.out.println(subject.settings);
assertThat(subject.settings, is(nameRule.getMethodName()));
}
@Test
public void test2() {
Bean subject = context.getBean(Bean.class);
System.out.println(subject.settings);
assertThat(subject.settings, is(nameRule.getMethodName()));
}
}
@Configuration
class LiveConfig {
@org.springframework.context.annotation.Bean
public String getSettings() {
return "/some/real/file.txt";
}
@org.springframework.context.annotation.Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Bean getBean() {
return new Bean();
}
}
@Configuration
class DevConfig {
private String settings;
@org.springframework.context.annotation.Bean
@Profile("Dev")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public String getSettings() {
return settings;
}
public void setSettings(String settings) {
this.settings = settings;
}
}
class Bean {
public Bean() {
System.out.println("Bean");
}
String settings;
@Autowired
void setSettings(String settings) {
System.out.println("Settings: " + settings);
this.settings = settings;
}
}
这使用Profile来更改Live看到的内容和测试的内容,并使用NameRule来获取名称。它很笨重。
我不会使用TestName规则,而是使用TemporaryFolder规则,并使用它来设置应用程序用于输出文件夹的任何设置。在非常罕见的情况下(即全面集成测试),我也只在测试中使用DI。
答案 1 :(得分:1)
我最终创建了注册BeanPostProcessor的ContextCustomizerFactory
package com.company.testing.base.spring;
import java.util.List;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
public class TestAwareContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(
Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
return (context, mergedConfig) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.addBeanPostProcessor(
new TestInstanceAwareBeanPostProcessor(mergedConfig.getTestClass()));
};
}
}
TestInstanceAwareBeanPostProcessor
public class TestInstanceAwareBeanPostProcessor implements BeanPostProcessor {
private final Class<?> testClass;
TestInstanceAwareBeanPostProcessor(Class<?> testClass) {
this.testClass = testClass;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof TestClassAware) {
((TestClassAware) bean).setTestClass(testClass);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
resources / META-INF / spring.factories
# ContextCustomizerFactories for the Spring TestContext Framework
org.springframework.test.context.ContextCustomizerFactory = \
com.company.testing.base.spring.TestAwareContextCustomizerFactory
答案 2 :(得分:0)
你的意思是这样吗?
public class MyTest {
@Test
public void testName() {
MyBean b = new MyBean(MyTest.class.getSimpleName());
b.doSomething();
}
}
答案 3 :(得分:0)
您可以使用Spring Boot的自动配置功能,以更优雅的方式实现此目的,方法是:
定义一个Configuration
类,该类以这种方式公开或注册您的bean:
@Configuration
public class MyBeanProviderConfiguration {
@ConditionalOnMissingBean
@Bean
public MyBean myBean() {
// return a fully initialised MyBean instance
}
}
然后定义一个自定义注释 Spring Boot之类的,用这种方式说@AutoConfigureMyBean
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ImportAutoConfiguration(MyBeanProviderConfiguration.class)
public @interface AutoConfigureMyBean {}
然后您可以在春季测试中使用它,下面是一个示例:
@RunWith(SpringRunner.class)
@AutoConfigureMyBean
public class MyTest {
@Autowired
MyBean myBean;
}
或者在常规的Spring测试中(使用Config类)声明与您的 MyBean @Autowired
相关的bean,MyBean
实例将自动注入到其中。