Spring batch test for dynamically created job - java

In my application I have multiple jobs So i created Dynamic jobs.I have no issue in running this application. I want to do unit testing for the dynamically created job.
I want to set my job to JobLauncherTestUtils .
#RunWith(SpringRunner.class)
#SpringBatchTest()
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#PropertySource("classpath:application.yml")
public class SpringBatchIntegrationTest {
#Inject
private JobRepository jobRepository;
#Inject
private JobLauncher mJobLauncher;
private JobLauncherTestUtils jobLauncherTestUtils;
#Inject
BatchJobConfig mBatchJobConfig;
public void initailizeJobLauncherTestUtils() {
jobLauncherTestUtils = new JobLauncherTestUtils();
jobLauncherTestUtils.setJobRepository(jobRepository);
jobLauncherTestUtils.setJob(mBatchJobConfig.createJob());
jobLauncherTestUtils.setJobLauncher(mJobLauncher);
}
This is how im initializing JobLauncherTestUtils . When I run this I get below error
Error creating bean with name 'jobLauncherTestUtils': Unsatisfied dependency expressed through method 'setJob' parameter 0; Can anyone tell me how do I do spring batch test for dynamic jobs.
I don't have much knowledge about Junit. I just started to learn

The #SpringBatchTest already adds a bean of type JobLauncherTestUtils in your test context (See Javadoc), so you don't need to add it yourself.
However, JobLauncherTestUtils requires a job bean, and it looks like you don't have one defined in your test context. What you can do is define one in a configuration class and import it in your test context, something like:
#RunWith(SpringRunner.class)
#SpringBatchTest
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#PropertySource("classpath:application.yml")
#ContextConfiguration
public class SpringBatchIntegrationTest {
#Inject
private JobRepository jobRepository;
#Inject
private JobLauncher mJobLauncher;
#Inject
private JobLauncherTestUtils jobLauncherTestUtils;
// No need for initailizeJobLauncherTestUtils
// Add your test method
#Configuration
#Import(BatchJobConfig.class) // you might need this or not depending on what's defined in BatchJobConfig
static class MyJobConfiguration {
#Bean
public Job job(BatchJobConfig mBatchJobConfig) {
return mBatchJobConfig.createJob();
}
}
}

Related

Micronaut custom context in test using #Factory

I'm trying to find any analogue of spring's #ContextConfiguration annotation in micronaut framework
Is there any way to run unit test in micronaut using custom application context?
My project is a library without main class.
I have #Factory with initialization of my beans.
#Factory
public class TestConfig {
#Singleton
public QueryParameterParser queryParameterParser() { }
#Singleton
public ConnectionHolder connectionHolder(DataSource dataSource) { }
#Singleton
public QueryExecutor queryExecutor(ConnectionHolder connectionHolder, QueryParameterParser parameterParser) { }
}
In spring I'd write these for using context in test:
#ContextConfiguration(classes = TestConfig.class)
public class FunctionTest {
#Autowired
private TransactionExecutor transactionExecutor;
The only way to do it in micronaut I've found is to create CustomContextBuilder:
#Introspected
public class CustomContextBuilder extends DefaultApplicationContextBuilder {
private TestConfig factory = new TestConfig();
public CustomContextBuilder() {
TestConfig.DataBase db = factory.db();
DataSource dataSource = factory.dataSource(db);
singletons(
db, dataSource,
queryParameterParser,
connectionHolder,
new MicronautFixturesTestExecutionListener()
);
}
and pass CustomContextBuilder to #MicronautTest annotation. It works but is too complicated to write code for instantiating every bean in CustomContextBuilder everytime.
Is there any way to pass my #Factory class to context and not to write all singletons?
I've tried package method and passed my #Factory package - but it didn't created context properly.

can we use #Autowired without bean registration?

In the code below, surprisingly, when I use #AutoWired, the fields are set but this class has not been registered as a bean, and the program works correctly, but when I took the MockingTest bean from the context, it said that there is no such bean. Is it possible to use AutoWired without registering a class as a bean?
#SpringBootTest
public class MockingTest {
#Autowired
private ApplicationContext context;
#Autowired
private CollegeStudent collegeStudent;
#Autowired
private StudentGrades studentGrades;
#Mock
private ApplicationDao applicationDao;
#InjectMocks
private ApplicationService applicationService;
#BeforeEach
void setUp() {
collegeStudent.setFirstname("Al");
collegeStudent.setLastname("Zam");
collegeStudent.setEmailAddress("555#gmail.com");
collegeStudent.setStudentGrades(studentGrades);
}
#Test
#DisplayName("mockito testing")
public void testMocking() {
when(applicationDao.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()))
.thenReturn(100.0);
assertEquals(100.0, applicationService.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()));
verify(applicationDao).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
verify(applicationDao,times(1)).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
}
}
#SpringBootTest sets up multiple hooks into the test runner. One of them is to inject beans into #Autowired fields, without the test class itself being a bean.
#SpringBootTest actually does a lot of "magic" behind the scenes, just so that things just work like we might expect, without us thinking about them too much.

Why is the constructor method being called before setup

Here is my class under test:
KafkaProcessorApplication
#EnableBinding(Processor.class)
#EnableConfigurationProperties(KafkaProperties.class)
public class KafkaProcessorApplication {
#Autowired
private Processor processor;
#Autowired
private KafkaProperties kafkaProperties;
private KafkaTemplate<String, String> kafkaTemplate;
#Autowired
KafkaProcessorApplication(SenderConfig senderConfig) {
this.kafkaTemplate = senderConfig.kafkaTemplate();
}
Here, SenderConfig is a just a simple config class with the method kafkaTemplate() creating a new instance of KafkaTemplate.
SenderConfig
#Configuration
public class SenderConfig {
#Autowired
KafkaProperties kafkaProperties;
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(new HashMap());
}
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(ProducerFactory()));
}
}
Here is the test class:
KafkaTestClass
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = {SenderConfig.class, KafkaProcessorApplication.class})
#TestPropertySource(locations = "classpath:test-resources.properties")
#RunWith(SpringRunner.class)
public class KafkaProcessorApplicationTest {
#Autowired
private Processor processor;
#Mock
private SenderConfig senderConfig;
#Mock
private KafkaProperties kafkaProperties = new KafkaProperties();
#Mock private KafkaTemplate mockKafka;
#Autowired
#InjectMocks
private KafkaProcessorApplication app;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("ServerConfig").when(kafkaProperties).getServersConfig();
when(senderConfig.kafkaTemplate()).thenReturn(kafkaTemplate);
}
I want to mock kafkaTemplate. But, its instantiation is in constructor which is being executed even before the #Before is executed, where the logic of mocking the method is written.
Just curious why is the constructor being executed first, and also, how can I mock the method if this is the case?
What could be the approaches of mocking the kafkaTemplate, without using Powermock and without modifying the class under test as I can not change it?
When you use #SpringBootTest, the Spring dependency tree is resolved before the #Before method has a chance to execute. This includes constructing the KafkaProcessorApplication bean and its dependencies. This is why the constructor runs before #Before.
What you want is Spring's #MockBean to create and inject a mock bean in the application context.
This question has a great write-up of how you can use this: Difference between #Mock, #MockBean and Mockito.mock()
update
Now I see. The problem is that the KafkaProcessorApplication accesses the mock in its constructor before you can configure it.
This can be solved with a separate test Spring configuration that will return a configured SenderConfig mock bean as described here: Testing spring bean with post construct

How to integrate Spring Boot, Cucumber and Mockito?

There are ways online to integrate Cucumber with Spring Boot. But I cannot find how to do so with Mockito also. If I use the Cucumber runner and annotate the steps file with ContextConfiguration and SpringBootTest, the container injects the Autowired dependencies and its all fine. The problem is that dependencies annotated with Mock, MockBean and InjectMocks dont work. Anyone knows why it doesnt work and how to make it work?
EDIT: It is possible to instantiate the bean with mock(Bean.class), instead of using the Mock annotation. But what about features like MockBean and InjectMocks?
Runner
#RunWith(Cucumber.class)
#CucumberOptions(plugin = {"pretty", "html:build/cucumber_report/"},
features = "classpath:cucumber/",
glue = {"com.whatever"},
monochrome = true,
dryRun = false)
public class CucumberTest {
}
Steps
#ContextConfiguration
#SpringBootTest
public class CucumberSteps
{
#Autowired
private Bean bean;
#InjectMocks //doesnt work
private AnotherBean anotherBean;
#MockBean //doesnt work with #Mock also
MockedBean mockedBean;
#Given("^Statement$")
public void statement() throws Throwable {
MockitoAnnotations.initMocks(this); //doesnt work with or without this line
Mockito.when(mockedBean.findByField("value"))
.thenReturn(Arrays.asList());
}
//Given-When-Then
}
Runner :
#CucumberOptions(plugin = {"pretty"},
glue = {"com.cucumber.test"},
features = "x/y/resources")
public class CucumberTest {
}
Here we will create a class with #SpringBootTest, #RunWith(SpringRunner.class) to start load beans into spring context. Now will mock the spring beans whatever we want to mack at here
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringTest {
#MockBean
private Mockedbean mockedbean;
}
Now, we need to extend the SpringBootTest annotated testclass to CucumberSteps class, then autowire the mocked bean at here, will get the instance of mocked bean(Mockedbean). We can do autowire and get instance of other spring boot beans also(TestBean)
public class CucumberSteps extends SpringTest {
#Autowired
private Mockedbean mockedbean;
#Autowired
private TestBean testBean;
}

Unit test in Spring: injecting a dependency into a component under test

I have a very simple rest controller:
#RestController
public class MyController {
#Autowired
public Logger logger;
The logger dependency gets injected via the following configuration:
#Configuration
public class MyConfig {
#Bean
public Logger logger() {
return LoggerFactory.getLogger(MyController.class);
}
If I run the Spring application that contains the controller then everything works fine. However, I cannot manage to achieve this dependency injection when running my unit tests. In this case I have the following test configuration:
#Configuration
#Profile("test")
public class MyTestConfig {
#Bean
public Logger logger() {
return LoggerFactory.getLogger(MyCOntroller.class);
}
And this is the relevant part of my unit tests code:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(classes = MyTestConfig.class)
#ActiveProfiles("test")
public class MyContollerTest {
However the logger object does not get "autowired" in MyController (note that I do not want to mock the logger object), which results in a null pointer reference.
What am I missing?
A unit test shouldn't use any Spring configuration. You should simply instantiate your component, and inject dependencies (usually fake ones) manually.
You used field injection, which makes it a bit harder. With constructor injection, all you would need to do is
Logger logger = LoggerFactory.getLogger(MyController.class);
MyController controller = new MyController(logger);
Mockito can help injecting fake dependencies for you, though, even when using field injection, thanks to the #Mock, #Spy and #InjectMocks annotations:
#Spy
private Logger logger = LoggerFactory.getLogger(MyController.class);
#InjectMocks
private MyController controller;
#Before
public void prepare() {
MockitoAnnotations.initMocks(this);
}
That said, if I'm not mistaken, you're not using #RunWith(SpringJUnit4ClassRunner.class), so your test runner doesn't know anything about Spring, and thus doesn't create or use any Spring configuration.

Categories

Resources