I am registering an ErrorHandler for my Spring Scheduler and would like to test that is is correctly registered in a SpringTest
So far I have tried:
Handler
#Component
public class ScheduledErrorHandler implements ErrorHandler {
#Autowired
private ErrorService errorService;
#Override
public void handleError(final Throwable t) {
errorService.handle(t);
}
}
Registering the Handler
#EnableScheduling
#Configuration
public class SchedulingConfiguration implements SchedulingConfigurer {
#Autowired
private ScheduledErrorHandler handler;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setErrorHandler(handler);
scheduler.initialize();
taskRegistrar.setScheduler(scheduler);
}
//...
}
Testing it's registered
#ContextConfiguration(classes = {
SchedulerConfiguration.class,
SchedulerErrorHandler.class
})
#RunWith(SpringRunner.class)
public class SchedulerErrorHandlerTest {
#MockBean
private ErrorService service;
#Autowired
private ExampleScheduledJob job;
#Test
public void verifyHandlerGetsCalled() {
// Wait until the job runs
if(!job.latch.await(5, SECONDS)) {
fail("Job never ran");
}
verify(service).handle(any(RuntimeException.class));
}
#Component
public static class ExampleScheduledJob {
private final CountDownLatch latch = new CountDownLatch(1);
#Scheduled(fixedRate=1000)
public void run() {
latch.countDown();
throw new RuntimeException("error");
}
}
}
However when I do this I get a DependencyNotFound error saying Spring cannot create my test class as no Bean named ExampleScheduledJob can be found. How can I register it only for the sake of this test?
Error creating bean with name
'com.example.demo.SchedulerErrorHandlerTest': Unsatisfied dependency
expressed through field 'job'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'com.example.demo.SchedulerErrorHandlerTest$ExampleScheduledJob'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
This should work
#ContextConfiguration(classes = {
SchedulingConfiguration.class,
SchedulerErrorHandlerTest.ExampleScheduledJob.class,
ScheduledErrorHandler.class
})
#RunWith(SpringRunner.class)
You can register your test configuration class (ExampleScheduledJob) as indicated above. Since it is a static inner class, you need to use it like SchedulerErrorHandlerTest.ExampleScheduledJob
Related
I'd like to create an integration test for my Spring Boot application checking that a controller returns the correct HTTP status when sending an email.
This is how my test looks like:
#SpringBootTest
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
#Profile("test")
public class EmailControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Rule
public SmtpServerRule smtpServerRule = new SmtpServerRule(2525);
private static final String RESOURCE_PATH = "/mail";
#Test
public void whenValidInput_thenReturns200() throws Exception {
final EmailNotification emailNotification = EmailNotification.builder()
.emailAddress("foo#bar.com")
.subject("TEST_SUBJECT")
.content("TEST_CONTENT")
.build();
mockMvc.perform(post(RESOURCE_PATH)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(emailNotification))
).andExpect(status().isOk());
}
}
However it fails with the following excetpion:
No qualifying bean of type 'org.springframework.test.web.servlet.MockMvc' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I looked at the Spring Boot tutorials on integration testing but cannot see what's wrong.
This is the controller under test:
#RestController
public class EmailController {
private static final Logger LOG = LoggerFactory.getLogger(EmailController.class.getName());
private final EmailService emailService;
#Autowired
public EmailController(EmailService emailService) {
this.emailService = emailService;
}
#PostMapping(value = "/mail", consumes = MediaType.APPLICATION_JSON_VALUE)
public void send(#Valid #RequestBody EmailNotification emailNotification) {
try {
emailService.sendEmail(emailNotification);
} catch (MailException | MessagingException e) {
LOG.error("Error sending email: (recipient address: {}): {}", emailNotification.getEmailAddress(), e.getMessage());
}
}
}
Remove the #Profile and add #ActiveProfiles("test") from your test class they are not the same.
#Profile conditional evaluates the bean if the profile passed as argument is enabled, #ActiveProfiles change the profile to the profile passed as a argument, or in other words, activate it.
I am trying to disable real Mongo connection and replace it with Fongo mock in tests.
Here is my test class:
#SpringBootTest
#RunWith(SpringRunner.class)
public class ControllerTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private MyService service;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void performTest() throws Exception {
... logic ...
}
}
It works fine unless I try to add my configuration file changing this line:
#SpringBootTest
to this:
#SpringBootTest(classes = TestConfig.class)
config class itself:
#Configuration
#ComponentScan
#EnableMongoRepositories
public class TestConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "FongoDB";
}
#Override
public Mongo mongo() {
return new Fongo(getDatabaseName()).getMongo();
}
}
Then application fails to find beans and throws the next exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.ObjectMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 28 more
How can I fix it and apply additional configuration properly?
try use
#SpringBootTest
#Import(value = TestConfig.class)
instead of
#SpringBootTest(classes = TestConfig.class)
keep #SpringBootTest and then create a class using #TestConfiguration with the beans in as follows:
#TestConfiguration
public class TransactionManagerTestConfiguration {
#Bean
public String getDatabaseName() {
return "FongoDB";
}
#Bean
public Mongo mongo() {
return new Fongo(getDatabaseName()).getMongo();
}
}
As per the javadoc: Configuration that can be used to define additional beans or customizations for a test. Unlike regular Configuration classes the use of TestConfiguration does not prevent auto-detection of SpringBootConfiguration.
I have a spring boot 2.0.0 M2 application who run well.
I use autowired on constructor
#RequestMapping(value = "/rest")
#RestController
public class AddressRestController extends BaseController{
private final AddressService AddressService;
#Autowired
public AddressRestController(final AddressService AddressService) {
this.AddressService = AddressService;
}
...
}
#Service
public class AddressServiceImpl extends BaseService implements AddressService {
#Autowired
public AddressServiceImpl(final AddressRepository AddressRepository) {
this.AddressRepository = AddressRepository;
}
private final AddressRepository AddressRepository;
...
}
public interface AddressRepository extends JpaRepository<Address, Integer>, AddressRepositoryCustom {
}
#Repository
public class AddressRepositoryImpl extends SimpleJpaRepository implements AddressRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Autowired
public AddressRepositoryImpl(EntityManager em) {
super(Address.class, em);
}
...
}
When i try to run a basic test
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
#Autowired
private AddressService service;
#MockBean
private AddressRepository restTemplate;
#Test
public void getAddress(){
MockitoAnnotations.initMocks(this);
Pageable page = PageRequest.of(0, 20);
Page<Address> pageAdr = mock(Page.class);
given(this.restTemplate.findAll(page)).willReturn(pageAdr);
Page<AddressDto> pageDto = service.getAddress(page);
}
}
I get this error
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name
'com.sonos.arcor.service.AddressServiceTest': Unsatisfied dependency
expressed through field 'service'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type ''com.sonos.arcor.service.AddressService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I don't understand why i get this error.
You need to annotate the test with SpringBootTest so that spring initialize an application context
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications
#SpringBootTest
#RunWith(SpringJUnit4ClassRunner.class)
public class AddressServiceTest {
// the remaining test
}
Also you do not need MockitoAnnotations.initMocks(this);
Spring takes care of the mock handling
When [#MockBean is]used on a field, the instance of the created mock will also be
injected. Mock beans are automatically reset after each test method
see Mocking and spying beans
I am getting exception -
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.muztaba.service.VerdictServiceImpl] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:372)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1066)
at com.muztaba.service.App.task(App.java:35)
at com.muztaba.service.App.main(App.java:28)
This is the class from where I am getting there exception.
#Component
public class App {
QueueService<Submission> queue;
Compiler compiler;
VerdictService verdictService;
public static void main( String[] args ) {
new App().task();
}
private void task() {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
queue = context.getBean(QueueImpl.class);
compiler = context.getBean(CompilerImpl.class);
verdictService = context.getBean(VerdictServiceImpl.class); //here the exception thrown.
while (true) {
if (!queue.isEmpty()) {
Submission submission = queue.get();
compiler.submit(submission);
}
}
}
}
First two variable injected properly but the verdictService is not.
this is my VerdictService and VerdictServiceImpl interface and class.
public interface VerdictService {
void post(Verdict verdict);
}
==
#Service
#Transactional
public class VerdictServiceImpl implements VerdictService {
#Autowired
SessionFactory sessionFactory;
#Override
public void post(Verdict verdict) {
sessionFactory.getCurrentSession()
.save(verdict);
}
}
and this is my configuration class
#Configuration
#EnableScheduling
#ComponentScan(basePackages = "com.muztaba")
public class AppConfig {
}
I have also give my project directory structure.
What I am missing here ? thank you.
You need to autowire VerdictService
#Autowired
VerdictService verdictService;
and you need to omit the line
verdictService = context.getBean(VerdictServiceImpl.class);
Ideally your code should use the services via autowiring.
it seems since you have annotated it with #transactional, Spring is creating a JDK interface based proxy. So spring manages a bean 'VerdictService' not 'VerdictServiceImpl'.
it should be
verdictService = context.getBean(verdictService.class);
instead of
verdictService = context.getBean(VerdictServiceImpl.class);
I'm trying to autowire a service implementation in one of my beans, but I keep getting a NoSuchBeanDefinitionException. This is my code:
Repository:
#Repository
public interface GlobalPropertiesRepository extends BaseRepository<GlobalProperties, Long>{
}
Service:
public interface GlobalPropertiesService {
GlobalProperties findOne(Long id);
}
Base Repository:
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import java.io.Serializable;
import java.util.List;
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
List<T> findAll(Iterable<ID> ids);
T findOne(ID id);
T save(T persisted);
<S extends T> S saveAndFlush(S entity);
}
Service Impl:
#Service("globalPropertiesService")
public class GlobalPropertiesServiceImpl implements GlobalPropertiesService{
#Autowired
GlobalPropertiesRepository globalPropertiesRepository;
#Override
public GlobalProperties findOne(Long id) {
return globalPropertiesRepository.findOne(id);
}
}
And then I autowire the implementation in one of my beans as below:
public class GlobalPropertiesLoader {
#Autowired
private GlobalPropertiesService globalPropertiesService;
private GlobalProperties globalProperties;
#PostConstruct
public void init(){
globalProperties = globalPropertiesService.findOne(1L);
}
public GlobalProperties getGlobalProperties(){
return globalProperties;
}
}
Finally, this is my Configuration class:
#Configuration
public class AppServiceConfig {
public AppServiceConfig() {
}
// Global properties
#Bean(name="globalPropertiesLoader")
public GlobalPropertiesLoader globalPropertiesLoader(){
return new GlobalPropertiesLoader();
}
}
This is my SpringBoot class:
#SpringBootApplication
#ComponentScan(basePackages="...")
public class TrackingService {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackingService.class);
static AnnotationConfigApplicationContext context;
public static void main(String[] args) throws Exception {
SpringApplication.run(TrackingService.class, args);
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(true);
String basePackage = "...";
Set<BeanDefinition> components = provider.findCandidateComponents(basePackage);
for (BeanDefinition component : components) {
LOGGER.info("Component: "+component.getBeanClassName());
}
context = new AnnotationConfigApplicationContext();
context.refresh();
context.close();
}
}
Now, when I try to start the application, I get the following error in my GlobalPropertiesLoader bean:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [GlobalPropertiesService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}