Replace bean at runtime

The codebase is a typical spring corporate codebase with approximately 1.5 million lines of code. We have quite a few spring context files. The problem with testing is the problem.

In test cases, I created another set of test-spring files (basically it imports the corresponding contexts of the spring project), and for several beans it contains mocked beans for external services. All test classes use the same set of context configuration files, and everything is fine in 90% of cases.

But in some cases there will be a bean that I would like to make fun of. But I do not want to edit spring -text.xml (since this will bother all classes), and I do not want to have a separate xml set for each test class. It is very simple to say that doing this would be:

@Autowired @Qualifier("fundManager") FundManager fundManager; @Test public void testSomething(){ TransactionManager tx = mock(TransactionManager.class); fundManager.setTransactionManager(tx); //now all is well. } 

This works in some cases. But sometimes it is desirable that this new temporary bean tx should be installed where ever the TransactionManager bean has been used throughout the code base.

The IMHO proxy class is a great solution because I would have to wrap all the beans in a wrapper. This is what I am ideally looking for:

 @Test public void testSomething(){ TransactionManager tx = mock(TransactionManager.class); replace("transactionManagerBean",tx); //For bean with id:transactionManagerBean should be replace with `tx` } 

BeanPostProcessor looks like an alternative suggestion, but I came across a few hiccups with it.

+7
java spring unit-testing
source share
1 answer

Imagine you have bean A introduced in bean B:

 public static class A { } public static class B { @Autowired private A a; @Override public String toString() { return "B [a=" + a + ']'; } } 

And spring context to initialize your application:

 <?xml version="1.0" encoding="UTF-8"?> <beans ...> <context:annotation-config/> <bean id="a" class="test.Test$A"/> <bean id="b" class="test.Test$B"/> </beans> 

Then the following snippet will replace all beans A in context:

 public static void main(String[] args) throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml"); System.out.println(ctx.getBean("b")); final A replacement = new A(); for (String name : ctx.getBeanDefinitionNames()) { final Object bean = ctx.getBean(name); ReflectionUtils.doWithFields(bean.getClass(), field -> { field.setAccessible(true); field.set(bean, replacement); }, // Here you can provide your filtering. field -> field.getType().equals(A.class) ); } System.out.println(ctx.getBean("b")); } 

This example was made with Java 8 + spring 4.1. However, it would be easy to change the code for older versions of both Java and Spring.

+4
source share

All Articles