What is the reason that the #Scheduled function runs in Spring Boot Application but not in test environment (#SpringBootTest) ?
I am following the tutorial at https://github.com/spring-guides/gs-scheduling-tasks, the repository's test runs fine, but mine #Scheduled function does not run for once in my test, although it is working fine in my Spring Boot application.
Is it because of the version of Junit (The tutorial is using Junit5 while I am using JUnit 4) ?
My purpose of this test is to check the correctness of the configuration of my scheduler.
Below is the code I replicated with difference in Junit version.
BootApplication.java
package com.itsedo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
// SecurityAutoConfiguration: exclude default spring security boot file.
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
#ComponentScan("com.itsedo")
#EnableScheduling
public class BootApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BootApplication.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(BootApplication.class, args);
}
}
ScheduledTasks.java
package com.itsedo.scheduler;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
#Component
public class ScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
File ScheduledTasksTest.java
package com.itsedo.test;
import org.awaitility.Duration;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.verify;
import com.itsedo.scheduler.ScheduledTasks;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ScheduledTasksTest {
#SpyBean
ScheduledTasks tasks;
#Test
public void reportCurrentTime() {
await().atMost(Duration.TEN_SECONDS).untilAsserted(() -> {
verify(tasks, atLeast(2)).reportCurrentTime();
});
}
}
Test Output
Running Spring Boot application
Related
I have been working on this for long and could not figure out a way in mocking the main class in my Spring Boot application (with the code as provided below); without directly calling the actual class in my test class (which I need to avoid).
I've tried with Power Mockito and MockRunner, but doesn't seem to work. How can I proceed?
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
#SpringBootApplication(exclude = KafkaAutoConfiguration.class)
public class KafkaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaConsumerApplication.class, args);
}
}
P.S. I'm furnishing the original test class I've written for the same, which is not appropriate way to test
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = KafkaConsumerApplication.class)
#ActiveProfiles("dev")
public class KafkaConsumerApplicationTest {
#Test
public void contextLoads() {
}
}
I have #SpringBootApplication with #ComponentScan({"myPackage"}) and in myPackage I have a class annotated with #Configuration or #Component. When I start the spring boot app the logs show:
DEBUG [main] org.sprin.conte.annot.ClassPathScanningCandidateComponentProvider 437 scanCandidateComponents: Identified candidate component class: file [C:\Web\project\bin\main\myPackage\Config.class]
but then nothing injects the class or its beans into the app...
It looks related to this
CODE
package app;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.gemfire.config.annotation.EnableLogging;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;
#SpringBootApplication
#ComponentScan({"myPackage"})
#EntityScan({"myPackage"})
#EnableGemfireRepositories("region")
#EnableLogging(logLevel="info", logFile="geodeApi.log")
public class Web {
private static final Logger log = LogManager.getLogger(Web.class);
public static void main(String[] args) {
log.info("In Main");
SpringApplication app = new SpringApplication(Web.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
SpringApplication.run(Web.class, args);
log.info("Out Main");
}
}
In myPackage.Client
package myPackage;
import java.util.UUID;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.apache.logging.log4j.Logger;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.logging.log4j.LogManager;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.gemfire.cache.config.EnableGemfireCaching;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnableClusterDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
import org.springframework.data.gemfire.config.annotation.EnablePool;
import org.springframework.data.gemfire.config.annotation.EnablePool.Locator;
import org.springframework.data.gemfire.config.annotation.EnableStatistics;
import org.springframework.session.data.gemfire.config.annotation.web.http.EnableGemFireHttpSession;
#ClientCacheApplication(name = "Web", logLevel = "debug")
#EnablePool(name = "webPool", subscriptionEnabled = true)
#EnableClusterDefinedRegions(clientRegionShortcut=ClientRegionShortcut.CACHING_PROXY)
#EnablePdx
#EnableStatistics
#EnableGemFireHttpSession(poolName = "webPool")
#EnableGemfireCaching
// #EnableWebFlux
public class Client {
private static final Logger log = LogManager.getLogger(Client.class);
#Resource
private Region<String, String> myAdmin;
#PreDestroy
public void onDestroy() throws Exception {
log.info("onDestroy");
String guid = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
myAdmin.put(guid, "Web Shutdown");
log.traceExit();
}
#Bean
ApplicationRunner StartedUp(){
log.traceEntry("StartedUp");
return args -> {
String guid = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
myAdmin.put(guid, "Web Started");
log.traceExit();
};
}
// Required to resolve property placeholders in Spring #Value annotations.
#Bean
static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
log.traceEntry("propertyPlaceholderConfigurer");
return new PropertySourcesPlaceholderConfigurer();
}
}
In myPackage.Config
package myPackage;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
#Configuration
public class Config {
private static final Logger log = LogManager.getLogger(Config.class);
#Bean("myRegion")
public Region<String, Object> myRegion(GemFireCache cache) {
log.traceEntry();
Region<String, Object> r = cache.getRegion("myRegion");
r.setUserAttribute(ClientRegionShortcut.CACHING_PROXY);
return r;
}
}
In Config class while defining the bean you are using Region<String, Object> as return type. Where as in the your Client class you define Region<String, String>. Here it is clearly a type mismatch and hence bean will not load.
Trying to modify an application to have a #Component that has a scheduled task to fire every n seconds. It seems like the executor service is never getting started to recognized there are #Components that have a #Scheduled annotation. Any ideas?
Ensured the packages are correct and should be in the componentscan base package.
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* This is the Spring Boot class for the Data Consistency Model (DCM)
*/
#EnableScheduling
#SpringBootApplication
#EnableConfigurationProperties({KafkaConfig.class, SparkConfig.class, JedisConfig.class, PrometheusConfig.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Component Class:
package com.test;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.PushGateway;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
#Component
#EnableScheduling
#EnableAsync
public class PrometheusGatewayMetricsPusher {
private static final Logger LOGGER = LogManager.getLogger(PrometheusGatewayMetricsPusher.class);
#Value("${spring.application.name}")
private String appName;
#Autowired
PushGateway pushGateway;
#Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void push() {
try {
pushGateway.push(CollectorRegistry.defaultRegistry, appName);
} catch (IOException e) {
LOGGER.log(Level.ERROR, "Error pushing to gateway. " + e.getMessage());
}
}
}
Config Class:
package com.test;
import io.prometheus.client.exporter.PushGateway;
import io.prometheus.client.hotspot.DefaultExports;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
#Getter
#Setter
#Configuration
public class MetricsConfig {
#Value("prometheus.config.gateway")
private String pushGatewayURL;
#PostConstruct
public void defaultExports() {
DefaultExports.initialize();
}
#Bean
public PushGateway pushGateway() {
return new PushGateway("localhost:9091");
}
}
I would expect the executor service to be started and initialized in the Application Context with a Thread Pool of 1. Then it would execute the #Scheduled method every second after an initial delay of 1 second. If I breakpoint in the #Component class it isn't being initialized at all.
I'm following this tutorial and I'm having some trouble starting my application.
When I run mvn spring-boot:run in the backend folder I get the following error:
Field movieRepository in com.movieseat.services.impl.MovieServiceImpl required a bean of type 'com.movieseat.repositories.MovieRepository' that could not be found.
MovieServiceIml.java
package com.movieseat.services.impl;
// Java imports
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// Spring imports
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// Project imports
import com.movieseat.models.Movie;
import com.movieseat.services.MovieService;
import com.movieseat.repositories.MovieRepository;
#Service
public class MovieServiceImpl implements MovieService {
#Autowired private MovieRepository movieRepository;
#Override public List<Movie> getAllmovies() {
List<Movie> movies = new ArrayList<Movie>();
Iterator<Movie> iterator = movieRepository.findAll().iterator();
while (iterator.hasNext()) {
movies.add(iterator.next());
}
return movies;
}
}
MovieRepository.java
package com.movieseat.repositories;
// Java imports
import java.io.Serializable;
// Spring imports
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
// Project imports
import com.movieseat.models.Movie;
#Repository
public interface MovieRepository extends CrudRepository<Movie, Serializable> {}
The following structure is used:
com
movieseat
Application.java
controllers
MovieController.java
models
MovieModel.java
repositories
MovieRepository.java
services
impl
MovieServiceImpl.java
MovieService.java
And this is my Application.java:
package com.movieseat;
// Spring importss
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan({"com.movieseat.*"})
#EnableJpaRepositories("com.movieseat.repositories.*")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My thought is that using #EnableJpaRepositories("com.movieseat.repositories.*") would make all repositories accessible. But I'm doing something wrong.
Try it without * (wildcard),
#EnableJpaRepositories("com.movieseat.repositories")
Make sure you have the #Entity annotation on your Movie.
I have written this code :
It seems perfect but not working
When i run the code it runs fine
I hit the URL for (/) and only the greetings method is called
AOP method is not getting called
This is my main Class
DemoApplication.java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.aop.AOPSample;
com.services.TaskService;
#SpringBootApplication
#ComponentScan({"com.aop","com.services"})
#RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/")
public void greetings(){
new TaskService().startService();
System.out.println("In Greetings");
}
}
AOPSample.java
package com.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class AOPSample {
#Before("execution(public void com.services.TaskService.startService())")
public void beforeSampleCreation() {
System.out.println("Hello world");
}
}
TaskService.java
package com.services;
public class TaskService {
public void startService(){
}
public void endService(){
}
}
Output:
In Greetings