Destroying bean of a given package and create again before each cucumber scenario (test) to prevent state from leaking between scenarios - spring

I am trying to destroy the beans before each test. So that when a test start running it should create fresh beans to execute the test as required classes are Autowired.
I am using destroyBean method of ConfigurableListableBeanFactory.
#Autowired
private ConfigurableListableBeanFactory beanFactory;
val beanClass = Class.forName(beanDefinition.getBeanClassName());
val beans = beanFactory.getBeansOfType(beanClass);
beanFactory.destroyBean(bean);
I am expecting the constructor to be called before each test as I am destroying all the beans.
But it is not calling the constructor and using the old beans.

#Scope("cucumber-glue")
Placing the above annotation on top of each bean fixed the problem. Sample -
#Component
#Scope("cucumber-glue")
public class TestComponent {
}

Related

Spring inject test: Bean is not injected on test

I've created this custom test configuration:
#TestConfiguration
public static class RestTemplateTestConfiguration {
#Bean
#Primary
public static ApplicationDao applicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
// some stuff code
return mock;
}
}
I've set a breakpoint on applicationDao, but it's never reached, and therefore mock is never injected.
EDIT
ApplicationDao is an #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {
So, how could I override this #Repository annotated AplicationDao?
Currently, I'm getting this message when spring starts:
Skipping bean definition for [BeanMethod:name=applicationDao,declaringClass=net.gencat.transversal.espaidoc.functional.references.GroupReferencesTest$RestTemplateTestConfiguration]: a definition for bean 'applicationDao' already exists. This top-level bean definition is considered as an override.
Any ideas?
If your method applicationDao() is never called it means that your spring boot is not scanning the package where RestTemplateTestConfiguration is located.
The simplest solutions is to move the configuration under the same package (or its children) as the one that contains the class annotated with #SpringBootApplication.
OBS : This rule applies even though the configuration is in the test directory instead of main.
Another solution is to add #ComponentScan with the configuration package or to use #Import(RestTemplateTestConfiguration.class) at your spring boot test level.
SUGGESTION:
For your problem you can use:
#Mock
ApplicationDao applicationDao;
and if you have another service that uses this one to use:
#InjectMock
MyService myService;

Spring Scheduled task: Scope 'request' is not active for the current thread

I am writing a project with Spring Boot 1.5.8.
I have some Entity classes which are generated and contain bean (for Example FooBean) which only exists in request scope. What is important: i am not able to change that part of code. So assume that i have an Entty:
#Enitty
public class FooEntity{
#Transient protected FooBean fooBean;
}
and FooBean implementation:
#Component
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class FooBean {
...
}
I also have some part of code where i have method which should be runned by Spring CRON:
#Scheduled(cron = "0 0/2 * * * ?")
#Transactional(value = Transactional.TxType.REQUIRES_NEW)
void scheduledTask() {
...
}
What's important: in that scheduledTask i am saving some instances of FooEntity to DB.
And of course: When i try to invoke repository.saveAll() Spring is throwing an exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.fooBean ': Scope 'request' is not active for the current thread;
Is there any possibility to resolve that problem? How can I override this scoped bean so it will be available in not-request scope?
I think I'm writing a little bit later :) but you can create your own instance of this bean.
In spring boot you can simply create your own singleton instance of the bean like this:
#Configuration
#EnableScheduling
public class ApiConfig {
#Bean
#Primary
public FooBean fooBean(){
return new FooBean();
}
}
In case your bean has some dependencies in constructor (constructor autowired) which has to be autowired you can simply put those beans in method params and spring will provide it for you like this:
#Bean
#Primary
public FooBean fooBean(BeanToAutowire myBean){
return new FooBean(myBean);
}
In case your bean has no constructor autowired dependencies but annotation autowired (using #Autowired) you can simple do it like this:
#Bean
#Primary
public FooBean fooBean(AutowireCapableBeanFactory beanFactory){
FooBean bean = new FooBean(myBean);
beanFactory.autowireBean(bean); // here spring will autowire all dependencies for you
return bean;
}
It is not the most elegant way to do it but it works. Side effect is that there will be two instances of FooBean in app context but spring will use your instance because you set this instance as primary using #Primary annotation. Only beans which use Autowired annotation together with qualifier annotation can choose which instance of FooBean will be autowired.

Spring Batch : How to instantiate "Step" scope bean in Cucumber Testing?

How to instantiate "Step" scope bean of spring in Cucumber Testing?
SpringJUnit4ClassRunner uses #TestExecutionListeners to instantiate the step scoped beans for testing purpose.
I am trying get this behavior in Cucumber. Cucumber uses a #RunWith(Cucumber.class)
Is there anyway we can instantiate step scope bean?
Thanks in advance
I'm not familiar with Cucumber, but I have instantiated/tested step scope items using #RunWith(SpringJUnit4ClassRunner.class)
I would recommend including the StepScopeTestExecutionListener.class as well as the DependencyInjectionTestExecutionListener (if you're injecting any dependencies) in your #TestExecutionListeners annotation, e.g. #TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class })
In order to instantiate a step scope bean in the test class, first get an instance of the ExecutionContext by utilizing the MetaDataInstanceFactory.
For example:
ExecutionContext executionContext = MetaDataInstanceFactory.createJobExecution().getExecutionContext();
Once you can have an instance of the ExecutionContext, you'll need to make use of the JobLauncherTestUtils class (documentation: http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/JobLauncherTestUtils.html). You can launch a step by calling launchStep(), for example:
jobLauncherTestUtils.launchStep(<step_name>, <job_parameters>, <execution_context>);
Hope that helps!
By exploring some options i have made it workable for now.
Below is my workaround
Bean Configuration
#Bean(name = "abc")
#StepScope
public ABC getABC(){
return new ABC();
}
#ContextConfiguration(classes = AppConfiguration.class, loader = AnnotationConfigContextLoader.class)
public class StepDef {
#Autowire
ABC abc;
public StepDef{
StepExecution stepExecution = getStepExecution();
StepSynchronizationManager.register(stepExecution);
}
}
I am not sure how correct is this implementation. I have initialize the StepExecution to load my configuration in stepDef Constructor so my AutoWiring can work properly and i can run my test against it.
I need to follow same approach for all stepDef , may be i will write a super class and implement that in super constructor.
Do let me know if you see any concerns.
Thanks again

Spring boot #SpyBean tries to instantiate a new bean instead of spying the one in context

I'm trying to unit-testing a Spring batch job inside Spring boot using JUnit.
I wrote this test class where I want to spy the bean ItemReader :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=WebEnvironment.NONE)
#ActiveProfiles({"dev", "batch", "test-jobs"})
public class BatchJobTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
private #Autowired #Qualifier("contactDownloadAckJob") Job contactDownloadAckTaskJob;
#SpyBean
private ItemReader<CrsOscContact> reader;
#Test
public void testJob() throws Exception {
given(this.reader.read()).willReturn(new CrsOscContact());
//... blah blah blah
}
}
When I run this test, it seems that the #SpyBean annotation does not do its job, that should be proxying the ItemReader bean that's already present in the context, and so I obtain the (correct) exception because, as per definition, if the bean is not found it tries to instantiate a new bean of that type (and I have specified an interface) :
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.item.ItemReader]: Specified class is an interface
I'm pretty sure that the bean (of ItemReader type) is already in the context because :
debugging, I see that the target bean instantiation is correctly processed
if I change the #SpyBean annotation to an #Autowired annotation, the instance of previous point is correctly injected
Any hint? Thank you
It was a Spring Core issue, now fixed.
https://github.com/spring-projects/spring-boot/issues/7625
https://jira.spring.io/browse/SPR-15011

Reasons for autowired beans in spring being null

I've come across a problem that at run time autowired beans in a certain class are all null. I would like an answer that will cover all possibilities on why a spring autowired bean is not initialised.
Only java config is used, no xml files at all
New keywords are only used at bean definitions
All classes and fields are annotated appropriately
Eg:
#ComponentScan(...
#Configuration
public...
#Bean
public ... myBean(){
return new ...;
}
#Service
public ...
#Autowired
private ... myBean ;
And I guess this is when I am trying to use service;
#Configuration
public ...
// Constructor
service = new Service();
// Field
private Service service;
The most likely reason is object instance is not a spring bean (the one which you want to inject something) in other words: Spring does not know the instance, and therefore does not care about it
BTW: if Spring would know/take care of that instance and would not find a matching bean, then the default result for #Autowired is an exception

Resources