Spring Component with JUnitTests - spring

I am using a Spring #Component which implements
ApplicationListener<ContextRefreshedEvent>. This component runs on tomcat startup, but when I run the unit tests, it runs the Component again. Why is that happening?
Here is the component -
BackGroundServices implements Thread.
#Component
public class RunBackgroundServices implements ApplicationListener<ContextRefreshedEvent> {
private final BackgroundServices backgroundServices;
private ExecutorService executor;
#Autowired
public RunBackgroundServices(BackgroundServices backgroundServices) {
this.backgroundServices= backgroundServices;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
executor = Executors.newSingleThreadExecutor();
executor.submit(backgroundServices);
}
public void onApplicationEvent(ContextStoppedEvent event) {
executor.shutdown();
}
}

you can annotate the test like that
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestConfiguration.class}, loader = AnnotationConfigContextLoader.class)
public class MyTest{}
and then create your own configuration of jUnit tests when you can initialize only these beans that you want
#Configuration
public class TestConfiguration {
#Bean
poublic Xxx xxBean(){
}
}

Related

Why did #TestConfiguration not create a bean for my test?

My service
#Service
public class StripeServiceImpl implements StripeService {
#Override
public int getCustomerId() {
return 2;
}
}
My test
public class StripeServiceTests {
#Autowired
StripeService stripeService;
#TestConfiguration
static class TestConfig {
#Bean
public StripeService employeeService() {
return new StripeServiceImpl();
}
}
#Test
public void findCustomerByEmail_customerExists_returnCustomer() {
assertThat(stripeService.getCustomerId()).isEqualTo(2);
}
}
The error: java.lang.NullPointerException. I had checked and the stripeService is actually null.
Since you are autowiring you need an applicationcontext so that Spring can manage the bean and then can get injected in your class. Therefore you are missing an annotation to create the applicationcontext for your testclass.
I have updated your code and it works now(with junit 5 on your classpath). In the case dat you are using junit 4 it should be #RunWith(SpringRunner.class) instead of #ExtendWith(SpringExtension.class):
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class StripeServiceTests {
#Autowired
StripeService stripeService;
#TestConfiguration
static class TestConfig {
#Bean
public StripeService employeeService() {
return new StripeServiceImpl();
}
}
#Test
public void findCustomerByEmail_customerExists_returnCustomer() {
assertThat(stripeService.getCustomerId()).isEqualTo(2);
}
}

Spring injection: #MockBean #Repository is not injected

I'm trying to #MockBean a #Repository annotated class:
#Repository
public interface ApplicationDao extends MongoRepository<Application, String> {}
I'm injecting it into a #Service annotated class:
#Service
public class AuthorizationService {
private ApplicationDao appsDao;
private List<Application> allowedApplications;
#Autowired
public AuthorizationService(ApplicationDao appsDao) {
this.appsDao = appsDao; //<<MOCKED INJECTED BEAN>>
this.fillApplications();
}
private void fillApplications() {
this.appsDao.findAll() //<<MOCKED method>>
.forEach(entry -> {
this.allowedApplications.put(entry.getName(), entry);
});
}
public bool isAuthorized(Application application) {
return this.allowedApplications
.stream()
.anyMatch(app -> app.getId().equals(application.getId()));
}
}
My test mocking configuration looks like:
#RunWith(SpringRunner.class)
#SpringBootTest()
public class GroupReferencesTest {
private #Autowired AuthorizationService;
private #MockBean ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app)); //<<FAILS>>
}
}
Nevertheless, my mocked object is not injected. I mean, when my AuthorizationService calls its injected ApplicationDao is returns an empty list instead of my mocked list.
I've tried to use #MockBean(name="applicationDao") as well. The behavior is the same.
I've also tried to configure my mocked bean using this code:
#TestConfiguration
public class RestTemplateTestConfiguration {
#Bean("applicationDao")
#Primary
public static ApplicationDao mockApplicationDao() {
ApplicationDao mock = Mockito.mock(ApplicationDao.class);
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(mock.findAll())
.thenReturn(allowedApplications);
return mock;
}
}
However, it doesn't works right.
Application class is:
public class Application {
private String id;
//setters & getters
}
Any ideas?
First things first - the type of test. Answer: Unit test.
You are starting Spring context that manages a lifecycle of AuthorizationService and then you are trying to inject mock. What really happens is that Spring IoC container is injecting a real ApplicationDao (the one managed by Spring IoC container) into the AuthorizationService.
Solution:
Manage lifecyle of AuthorizationService by your test runner (like MockitoJUnitRunner and inject ApplicationDao mock into it):
#RunWith(MockitoJUnitRunner.class)
public class GroupReferencesTest {
private #InjectMocks AuthorizationService authorizationService;
private #Mock ApplicationDao applicationDao;
#Before
public void setUp() {
Application testApplication = new Application();
testApplication.setName("test-application");
List<Application> allowedApplications = new ArrayList<Application>();
allowedApplications.add(testApplication);
Mockito
.when(this.applicationDao.findAll())
.thenReturn(allowedApplications);
}
#Test
public void test() {
Application app = new Application();
app.getId("test-application");
assertTrue(this.authorizationService.isAuthorized(app));
}
}
Working example
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {AuthorizationService.class})
public class GroupReferencesTest {
#Autowired
private AuthorizationService;
#MockBean
private ApplicationDao applicationDao;
#Test
public void test() {
//given
Mockito.when(applicationDao.findAll()).thenReturn(emptyList());
//when & then
assertTrue(authorizationService.isAuthorized(app));
}
}

Unit testing of Spring ApplicationEventPublisher fails to consume the events

I want to integration test a Spring Boot 1.5.4 service that uses an #EventListener. My problem is: when the test is run, the events are correctly published, but they are not consumed.
My ultimate purpose is to use a #TransactionEventListener, but for simplicity I start with an #EventListener.
Here is my service class:
#Service
public class MyService {
private static final Logger logger = // ...
private final ApplicationEventPublisher eventPublisher;
#Autowired
public MyService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
#Transactional
public void publish() {
logger.warn("Publishing!");
eventPublisher.publishEvent(new MyEvent());
}
// #TransactionalEventListener
#EventListener
public void consume(MyEvent event) {
logger.warn("Consuming!"); // this is never executed in the test
}
public static class MyEvent {
}
}
Here is my JUnit test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#SpringBootConfiguration
public class MyServiceIT {
#Autowired
MyService myService;
#Test
public void doSomething() {
myService.publish();
}
static class TestConfig {
#Bean
public MyService myService() {
return new MyService(eventPublisher());
}
#Bean
public ApplicationEventPublisher eventPublisher() {
ApplicationEventPublisher ctx = new GenericApplicationContext();
((AbstractApplicationContext)ctx).refresh();
return ctx;
}
}
}
Note: the call to refresh() prevents an IllegalStateException with message "ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context" from occurring.
Does anyone have a clue? Many thanks in advance.
For the record, the solution was: keep the event consumer method in another bean than the event producer method. That is, extract method consume(MyEvent) from class MyService into a new #Service class MyConsumer.

Unit Testing using JUnit for Spring Batch without XML configuration

I am new to Spring Batch and I started developping a simple batch application. Now I am thinking of some unit testing unsing JUnit that could be healthy for my app and code ;)
The problem is that I couldn't find any ressource (examples, tutos ...) on the internet that shows how to perform unit testing with Spring Batch when using no XML.
Here is my code to be more clear :
Config class:
package my.company.project.name.batch.config
#Configuration
#EnableBatchProcessing
#ComponentScan({
"my.company.project.name.batch.reader",
"my.company.project.name.batch.tasklet",
"my.company.project.name.batch.processor",
"my.company.project.name.batch.writer"
})
#Import({CommonConfig.class})
public class MyItemBatchConfig {
#Autowired
private StepBuilderFactory steps;
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private MyItemTasklet myItemTasklet;
#Bean
public Job myItemJob(#Qualifier("myItem") Step loadProducts){
return jobBuilderFactory.get("myItemJob").start(myMethod).build();
}
#Bean(name= "myItem")
public Step myMethod(){
return steps.get("myItem").tasklet(myItemTasklet).build();
}
}
MyItemReader class :
package my.company.project.name.batch.reader
#Component
public class MyItemReader implements ItemReader<MyItem>{
#Value("${batch.load.produit.csv.file.path}")
private String csvFilePath;
private LinkedList<CsvRawLine> myItems;
#PostConstruct
public void init() {
myItems = new LinkedList<>(CsvUtil.getCsvReader(MyItem.class, csvFilePath));
}
#Override
public MyItem read() throws Exception{
return myItems.poll();
}
}
ItemProcessor class :
package my.company.project.name.batch.processor
#Component
public class MyItemProcessor implements ItemProcessor<MyItem, MyItemProcessorResult> {
public MyItemProcessorResult process(MyItemitem) throws Exception {
//processing business logic
}
}
ItemWriter class :
package my.company.project.name.batch.writer
#Component
public class MyItemWriter implements ItemWriter<MyItem> {
#Override
public void write(List<? extends MyItem> myItems) throws Exception {
//writer business logic
}
}
MyItemTasklet class that will call all the previous classes in order to achieve the task wanted by the batch:
package package my.company.project.name.batch.tasklet
#Component
public class MyItemBatchTasklet implements Tasklet{
#Autowired
public MyItemReader myItemReader;
#Autowired
public MyItemProcessor myItemProcessor;
#Autowired
public MyItemeWriter myItemWriter;
#Override
public RepeatStatus execute execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
//calling myItemReader, myItemProcessor and myItemWriter to do the business logic
return RepeatStatus.FINISHED
}
}
MyItemTasklet class that will launch the tasklet by its main method :
package package my.company.project.name.batch
public class MyItemTaskletLauncher{
public MyItemTaskletLauncher(){
//No implementation
}
public static void main (String[] args) throws IOException, JobExecutionException, NamingException {
Launcher.launchWithConfig("Launching MyItemTasklet ...", MyItemBatchConfig.class,false);
}
}
I made a simple batch application using Spring Batch and MyBatis and JUnit.
The test codes of application runs unit testing without XML.
Here is test class for Job.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {xxx.class, yyy.class, zzz.class, xxxJobLauncherTestUtils.class})
public class JobTest {
#Autowired
#Qualifier(value = "xxxJobLauncherTestUtils")
private JobLauncherTestUtils xxxJobLauncherTestUtils;
#Test
public void testXxxJob() throws Exception {
JobExecution jobExecution = xxxJobLauncherTestUtils.launchJob();
assertThat(jobExecution.getStatus(), is(BatchStatus.COMPLETED));
}
}
#Component(value = "xxxJobLauncherTestUtils")
class XxxjobLauncherTestUtils extends JobLauncherTestUtils {
#Autowired
#Qualifier(value = "xxxJob")
#Override
public void setJob(Job job) {
super.setJob(job);
}
}
About details, please see the below link.
https://github.com/Maeno/spring-batch-example/tree/master/src/test
I hope that it will be helpful.

Null Pointer when using #SpringBootTest

I am using spring boot 1.4,
when using the #SpringBootTest annotation for integration test, it gives a null pointer.
#RunWith(SpringRunner.class);
#SpringBootTest
public class MyControllerTest {
#Test
public void mytest {
when().
get("/hello").
then().
body("hello");
}
}
and for main class:
#SpringApplication
#EnableCaching
#EnableAsync
public class HelloApp extends AsyncConfigureSupport {
public static void main(String[] args) {
SpringApplication.run(HelloApp.class, args);
}
#Override
public Executor getAsyncExecutor() {
...
}
}
Then in my controller:
#RestController
public class HelloController {
#Autowired
private HelloService helloService;
#RequestMapping("/hello");
public String hello() {
return helloService.sayHello();
}
}
HelloService
#Service
public class HelloService {
public String sayHello() {
return "hello";
}
}
But it ways says NullPointException when for helloService when processing request.
What am I missing?
You need to mock HelloService in your test class as your controller is calling a service .Here in your case Your Test class is not aware that there is any service available or not
The following example test class might help you. In this guide from spring an example is shown how to integration test a rest controller in a spring fashion way.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class HelloControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void hello() throws Exception {
mockMvc.perform(get("/hello")).andExpect(content().string("hello"));
}
}

Resources