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

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

Related

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

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 {
}

How to achive #Capturing like behavior in MockK?

We have Spring application unit tested using JMockit mock framework. Now we would like to write new tests in Kotlin using MockK. Almost everything seems to work fine but we can't figure out how to mock beans autowired by Spring. With JMockit we used to use #Capturing annotation that extended the mocking also on classes implementing the mocked interface. How can I achive similar behavior of a mock in the MockK framework?
Bringing oleksiyp comments to an answer
Currently, Mockk doesn't have that kind of behavior. Its support to Spring is limited, but there's a workaround using Spring itself:
You can create a bean however you want, even in Integration Tests. When creating a bean, you can instantiate a mock:
#Bean
fun bean(): BeanType = mockk()
Then, when this bean is autowired, it will return the mocked instance, and you'll be able to set it's behavior using Mockk's DSL as usual.
Spring documentation recommends that all your components be autowired through the constructor. If you follow that convention you wouldn't have this problem.
To be specific, the recommendation is as following...
#RestController
class SomeRandomController(
#Autowired private val ARepository: aRepository,
#Autowired private val BRepository: bRepository,
#Autowired private val CRepository: cRepository
){ etc ...}
Then in your test you will need the following lines:
val aRepository = mockk<ARepository>(relaxed = true)
val bRepository = mockk<BRepository>(relaxed = true)
val cRepository = mockk<CRepository>(relaxed = true)
val controller = SomeRandomController(aRepository, bRepository, cRepository)

Inject autowired object as mock into spock test in spring boot application

I have a Spring Boot application and Service with private DAO field inside it. Private DAO property is annotated with #Autowired (no setters or constructors set it, just annotation).
I tried to write Spock test for service, but can't find how to inject mock DAO into #Autowired variable.
class TestService extends Specification {
DAO dao = Mock(DAO)
Service service = new Service()
def "test save"() {
when:
service.save('data')
then:
1 * dao.save('data')
}
}
Any ideas?
UPD: I'm testing java code.
As result I did this:
class TestService extends Specification {
DAO dao = Mock(DAO)
Service service = new Service()
void setup() {
service.dao = dao
}
def "test save"() {
when:
service.save('data')
then:
1 * dao.save('data')
}
}
One point was to use reflection. But Groovy can set private fields directly without additional manipulations. It was news for me.
sorry to bring a little over a year old thread to life but here is my two cents. Groovy does provide access to private fields even though it break encapsulation. Just incase if you haven't figured it out, when you manually instantiate a class with Autowired fields, Autowired fields will be null. You can either provide setters for it and set them or groovy can see private fields anyways. However, if you have luxury I would suggest to refactor it to use constructor injection and do the same for any of your code in future. Field Injection and setter injections have some problems when it comes to testing.

Prevent Application / CommandLineRunner classes from executing during JUnit testing

If in your TestCase class there is this annotations:
#SpringApplicationConfiguration(classes = {Application.class})
this will cause the Application.class, implementing the CommandLineRunner interface, to run the required method
public void run(String... args) throws Exception
I still think this is, mostly, a not wanted behaviour, since in your test environment you may not want to launch the entire application.
I have in mind two solution to circumvent this problem:
to remove the CommandLineRunner interface from my Application class
to have a different context for testing
Both this solution requires lot of coding.
Do you have a more convenient solution?
Jan's solution can be achieved easier.
In your test class, activate the "test" profile:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
public class MyFancyTest {}
In your CommandLineRunner set the profile to NOT test:
#Component
#Profile("!test")
public class JobCommandLineRunner implements CommandLineRunner {}
Then you don't have to manually set the profile in the Application.
As mentioned in the spring documentation you can use #ContextConfiguration with a special initializer:
ConfigDataApplicationContextInitializer is an ApplicationContextInitializer that you can apply to your tests to load Spring Boot application.properties files. You can use it when you do not need the full set of features provided by #SpringBootTest
In this example anyComponent is initialized and properties are injected, but run(args) methods won't be executed. (Application.class is my main spring entry point)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
initializers = ConfigDataApplicationContextInitializer.class)
public class ExtractorTest {
#Autowired
AnyComponent anyComponent;
#Test
public void testAnyComponent() {
anyComponent.anyMethod(anyArgument);
}
}
You can define a test configuration in the same package as your application that looks exactly the same, except that it excludes beans implementing CommandLineRunner. The key here is #ComponentScan.excludeFilters:
#Configuration
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class))
#EnableAutoConfiguration
public class TestApplicationConfiguration {
}
Then, just replace the configuration on your test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = TestApplicationConfiguration.class)
public class SomeApplicationTest {
...
}
No CommandLineRunner will be executed now, because they are not part of the configuration.
I'm a bit late to the party, but a reasonable approach is to mark the bean with #ConditionalOnProperty, e.g.
#ConditionalOnProperty(prefix = "job.autorun", name = "enabled", havingValue = "true", matchIfMissing = true)
public CommandLineRunner myRunner() {...}
The following annotation will then disable it in tests:
#SpringBootTest(properties = {"job.autorun.enabled=false"})
If you have a mocking framework installed (e.g. MockMVC) you can create a mock instance of the CommandLineRunner implementation, more or less disabling it:
#MockBean
private TextProcessor myProcessor;
Previous answers didn't work wor me. I ended up using different profiles - example for the init method in Spring Boot:
SpringApplication app = new SpringApplication(AppConfig.class);
app.setAdditionalProfiles("production");
app.run(args);
This is not executed during the tests so we're safe here.
All tests have their own profile "test" (which is useful in many other ways, too):
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
public class MyFancyTest {}
The command-line runner is annotated with the "production" profile so the tests ignore it:
#Component
#Profile("production")
public class JobCommandLineRunner implements CommandLineRunner {}
I solve this by not implementing CommandLineRunner. Just get a bean from the context, and call a method on it, passing argv. That way you will get the same result, and the application won't start automatically when running the tests.

Spring dynamic autowire services

I've started to build some kind of a CMS and I'm stuck over one idea.
The description is:
I have standard MVC Controller (Home) in which I'm downoading modules settings which will be set in this Controller.
The response is, that I have to implement module with name "HPModule".
So I'm trying to load this module by Class.forName("com.app.something.HPModule"); and then call method init();
My HPModule is:
public class HPModule
{
#Resource(name = "hpModuleService")
private HPModuleService hpModuleService;
public String init()
{
SomeObject someObject = hpModuleService.getArticle();
}
}
And I found that when I'm trying to do SomeObject someObject = hpModuleService.getArticle(); Spring is blind for #Resource when I'm calling class by Class.forName.
How to solve this issue?
The HPModule has to be a Spring Bean retrieved by means of DI or directly from Spring BeanFactory. You cannot expect Spring to autowire a class that is not instantiated by Spring, unless You use #Configurable and AspectJ to weave the class.
If HPModule already is a Spring Bean, than just #Autowire or #Inject it directly into the MVC controller that needs it.
If You don't know in compile time what modules You'll need, than inject ListableBeanFactory and use BeanFactoryUtils to get the modules You need in runtime by type or by name.

Resources