question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Improve Startup of Spring Integration Test

See original GitHub issue

It would be really useful to provide a native mechanism to tell spring boot test to mark all beans in the context as “lazy”. This can substantially speed up running an individual integration test, as you only have to wait for the initialization of beans that have actually been injected into the test. Even if this were an “opt-in” strategy, I think a lot of people would find it useful.

We solved this problem, in our environment, by adding a BeanFactoryPostProcessor that marks all beans within the application context as “lazy”. We add this processor only in our testing context and we have seen anywhere between a 10 and 20 seconds improvement in startup of individual tests. This also improves overall runtime when tests use mock beans or dirty the context, a subsequent test will start more quickly when the new application context is initialized.

This strategy works in a majority of cases except when using one of Spring’s DSLs (like spring integration). The problem with these is that the flows are not directly referenced by any class, but the beans they create ARE. We had two work arounds for this use case: You can @Autowire the flows within a test to insure your beans are created or you can add a list of class exclusions to the post processor.

I think there might be a more elegant way to do this but this is the post-processor we are using today:

/**
 * This post processor will mark all beans in the context as "lazy", meaning they will be loaded "on-demand".
 * This can substantially speed up running a single integration test, as it will ONLY initialize the beans references
 * by that test. This strategy works in a majority of cases except when using one of Spring's DSLs (like spring integration).
 * In those cases, those integration flows indirectly create additional beans that may be needed as dependencies in 
 * specific tests. This class can be initialized with a set of exclusion classes, such that beans that are are assignable
 * to one of the classes in the exclusion list will NOT be lazy initialized.
 * 
 * <pre>
 * Example:
 * 
 * {@code
 *	@Bean
 *	public BeanFactoryPostProcessor lazyBeanPostProcessor() {
 *		return new ServiceTestLazyBeanFactoryPostProcessor();
 *	}
 *
 *	static private class ServiceTestLazyBeanFactoryPostProcessor extends LazyInitBeanFactoryPostProcessor {
 *		public ServiceTestLazyBeanFactoryPostProcessor() {
 *			super(new Class<?>[] {IntegrationFlow.class});
 *		}	
 *	}
 * 
 * </pre>
 */
public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

	private Class<?>[] exclusionList;
	
	public LazyInitBeanFactoryPostProcessor() {
	}

	public LazyInitBeanFactoryPostProcessor(Class<?>[] exclusionList) {
		this.exclusionList = exclusionList;
	}
	
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
		//Iterate over all bean, mark them as lazy if they are not in the exclusion list.
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			if (isLazy(beanName, beanFactory)) {
	       		BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
	       		definition.setLazyInit(true);
			}
		}
	}
	
	private boolean isLazy(String beanName, ConfigurableListableBeanFactory beanFactory) {
		if (exclusionList == null || exclusionList.length == 0) {
			return true;
		}
		for (Class<?> clazz : exclusionList) {
			if (beanFactory.isTypeMatch(beanName,clazz)) {
				return false;
			} 
		} 
		return true;		
	}
}

Then within our Test:

@SpringBootTest(webEnvironment=WebEnvironment.NONE)
public abstract class BaseServiceTest extends BaseTest {

	@TestConfiguration	
	protected static class ServiceTestConfiguration {
		
		@Bean
		public BeanFactoryPostProcessor lazyBeanPostProcessor() {
			return new ServiceTestLazyBeanFactoryPostProcessor();
		}
		

		static private class ServiceTestLazyBeanFactoryPostProcessor extends LazyInitBeanFactoryPostProcessor {	
			public ServiceTestLazyBeanFactoryPostProcessor() {
				super(new Class<?>[] {IntegrationFlow.class});
			}	
		}
	}

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:6
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
tkvangordercommented, Jul 6, 2017

Makes sense to me, at the very least others can use my example if they run into a similar issue. Thanks for looking into it.

1reaction
dsyercommented, Dec 19, 2017

I think this might be worth a second look. It’s a very simple idea, but Bill says it works well for his app. It shaves more than 20% off the startup time of Petclinic even (which is a pretty simple app). The tests in Petclinic use slices though, so you don’t gain much really in practice for that app. But for large, slow apps where integration tests are the norm, it could be quite a big deal.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Optimizing Spring Integration Tests - Baeldung
In this article, we'll have a holistic discussion about integration tests using Spring and how to optimize them.
Read more >
Integration Test Speedup with Spring - Auto1 Tech Blog
Here is what we found on speeding up your integration tests in spring based applications.
Read more >
Spring Boot Integration Testing With @SpringBootTest
In this article, we look at how to write integration tests with @SpringBootTest . First, we will discuss different types of integration ......
Read more >
A quick guide to Spring Tests optimization - 10Pines | Blog
A quick guide to Spring Tests optimization · JVM initialization time · Tests depending on Spring Boot (integration tests among them) · Some ......
Read more >
Guide to @SpringBootTest for Spring Boot Integration Tests
@SpringBootTest is a powerful tool to write integration tests · not every part of your application should be tested with this expensive test...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found