Spring boot application does not find placeholder 'spring.embedded.kafka.brokers' for a spock-spring test - spring

I am trying to do an integration test with Spring Boot, Spock-Spring and Embedded Kafka. I followed this guide, where I found "As the embedded broker is started on a random port, we can’t use the fix value in the src/main/resources/application.yml properties file. Luckily the #ClassRule sets a spring.embedded.kafka.brokers system property to the address of the embedded broker(s)." Hence I have
#ContextConfiguration
#SpringBootTest(classes = [Application], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#DirtiesContext
#Stepwise
#ActiveProfiles("test")
class AnIntegrationTest extends Specification {
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, "topic")
private KafkaTemplate<String, String> template
def setup() {
Map<String, Object> senderProperties = KafkaTestUtils.senderProps(embeddedKafka.getBrokersAsString())
ProducerFactory<String, String> producerFactory =
new DefaultKafkaProducerFactory<String, String>(senderProperties)
template = new KafkaTemplate<>(producerFactory)
template.setDefaultTopic("topic")
for (MessageListenerContainer messageListenerContainer : kafkaListenerEndpointRegistry
.getListenerContainers()) {
ContainerTestUtils.waitForAssignment(messageListenerContainer, embeddedKafka.getPartitionsPerTopic())
}
}
def "test"() {
given:
template.sendDefault("Hello")
}
}
In application-test.yml I have
kafka:
bootstrapservers: ${spring.embedded.kafka.brokers}
In the application I start a Kafka consumer with spring-kafka.
Stack trace is
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.spockframework.spring.SpringTestContextManager.prepareTestInstance(SpringTestContextManager.java:50)
at org.spockframework.spring.SpringInterceptor.interceptSetupMethod(SpringInterceptor.java:42)
at org.spockframework.runtime.extension.AbstractMethodInterceptor.intercept(AbstractMethodInterceptor.java:28)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:88)
at org.spockframework.runtime.extension.builtin.AbstractRuleInterceptor$1.evaluate(AbstractRuleInterceptor.java:37)
at org.togglz.junit.TogglzRule$1.evaluate(TogglzRule.java:127)
at org.spockframework.runtime.extension.builtin.TestRuleInterceptor.intercept(TestRuleInterceptor.java:38)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kafkaListenerContainerFactoryConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.embedded.kafka.brokers' in value "${spring.embedded.kafka.brokers}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
... 19 more
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.embedded.kafka.brokers' in value "${spring.embedded.kafka.brokers}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.core.env.AbstractPropertyResolver.resolveNestedPlaceholders(AbstractPropertyResolver.java:227)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:84)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:61)
at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:527)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$1.getProperty(PropertySourcesPlaceholderConfigurer.java:132)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$1.getProperty(PropertySourcesPlaceholderConfigurer.java:129)
at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:81)
at org.springframework.core.env.PropertySourcesPropertyResolver.getPropertyAsRawString(PropertySourcesPropertyResolver.java:71)
at org.springframework.core.env.AbstractPropertyResolver$1.resolvePlaceholder(AbstractPropertyResolver.java:239)
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:147)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:831)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
... 36 more
Does anyone have a clue?

Ensure that in each of your integration test you setup both #SpringBootTest and #EmbeddedKafka.
This is common mistake when you run multiple integration tests at once with #SpringBootTest annotation. Since you have registered DefaultKafkaConsumerFactory and DefaultKafkaProducerFactory in your application configurator bean and probably you also use #Value("${spring.kafka.bootstrap-servers}") somewhere there, you are obligated to setup #EmbeddedKafka(partitions = 1, topics = {"topic"}) everywhere you call spring-boot application to run on test server container. #EmbeddedKafka is responsible to call EmbeddedKafkaContextCustomizer to register all environment variables (among others spring.embedded.kafka.brokers) needed by kafka engine.
Without #EmbeddedKafka in your Integration Test spring runs your application on server but there is no mechanism to set up environments used by kafka engine.

I invoked embeddedKafka.before() in setup(). Then the spring.embedded.kafka.brokers got set and available.
I am not sure, but the problem seems like Spock Spring specific.

I am not familiar with Spock but a simple Java JUnit test works fine...
#SpringBootApplication
public class So47172973Application {
public static void main(String[] args) {
SpringApplication.run(So47172973Application.class, args);
}
#KafkaListener(topics = "foo")
public void in(String in) {
System.out.println(in);
}
}
and
spring:
kafka:
bootstrap-servers:
- ${spring.embedded.kafka.brokers}
consumer:
group-id: embedded1
auto-offset-reset: earliest
and
#RunWith(SpringRunner.class)
#SpringBootTest
public class So47172973ApplicationTests {
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, "foo");
#Autowired
private KafkaTemplate<String, String> template;
#Test
public void test() throws InterruptedException {
template.send("foo", "bar");
Thread.sleep(10_000);
}
}
A couple of things to try:
Use the annotation instead...
#RunWith(SpringRunner.class)
#SpringBootTest
#EmbeddedKafka(controlledShutdown = true, topics = "foo")
public class So47172973ApplicationTests {
#Autowired
private KafkaTemplate<String, String> template;
#Test
public void test() throws InterruptedException {
template.send("foo", "bar");
Thread.sleep(10_000);
}
}
declare the broker as a #Bean...
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {So47172973ApplicationTests.Config.class, So47172973Application.class})
public class So47172973ApplicationTests {
#Autowired
private KafkaTemplate<String, String> template;
#Test
public void test() throws InterruptedException {
template.send("foo", "bar");
Thread.sleep(10_000);
}
#Configuration
public static class Config {
#Bean
public KafkaEmbedded embeddedKafka() {
return new KafkaEmbedded(1, true, "foo");
}
}
}

Got the similar issue and #taro's answer didn't resolve my issue.
I fixed this issue by removing static from EmbeddedKafka object and replacing #ClassRule with #Rule annotation.
#Rule
public KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, "topic")

Related

problem of bean injection RabbitTemplate in Junit with spring-boot 2

I have small problems, injection of a bean when I run in spring-boot everything works well but with Junit. I have an example with RabbitTemplate
public class NettyServerRun {
#Autowired
private IDeviceClient deviceClientComponent;
#Autowired
private ServiceDeviceServer service;
#Autowired
private RabbitManager mqService;
private int port = 7650;
#PostConstruct
public void init() throws InterruptedException {
NioEventLoopGroup boosGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boosGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
final EventExecutorGroup group = new DefaultEventExecutorGroup(1500);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(workerGroup,new
RequestDecoderServer(deviceClientComponent));
pipeline.addLast(workerGroup,new ResponseEncoderServer());
pipeline.addLast(group,new AuthenticateHandler(service));
pipeline.addLast(group,new CommandResponseHandler(service));
pipeline.addLast(group,new DeviceDataHandler(mqService));
}
});
ChannelFuture f = bootstrap.bind(port).sync();
f.channel().closeFuture().sync();
}
#RunWith(SpringRunner.class)
#DataJpaTest
#Import(NettyServerRun.class)
#AutoConfigureTestDatabase(replace=Replace.ANY)
#ComponentScan("com.metier")
public abstract class DeviceServerTest {
#Autowired
protected TestEntityManager entityManager;
#Autowired
protected ServiceDeviceServer service;
#Autowired
protected TestRestTemplate template;
protected DeviceServerContext context;
protected Gson gson;
protected Nmea nmeaData;
#Value("${spring.profiles.active}")
private String activeProfile;
protected void persistList(List<AbstractEntity> list) {
list.forEach(entity -> entityManager.persist(entity));
}
public GpsCmdRsp buidGpsCmdRsp(Long gpsId) {
GpsCmdRsp reference = new GpsCmdRsp();
reference.setCommand("*TS01,188765,NAM#");
reference.setCompletedAt(buildHier());
reference.setGpsId(gpsId);
reference.setResponse("*TS01,353836057694499,013809281017,NAM:ODO50-
BLE#");
reference.setSuccess(false);
return reference;
}
public class DeviceDataHandlerTest extends DeviceServerTest {
#Autowired
private NettyServerRun nettyServerRun;
#Before
public void setUp() {
}
#Test
public void channelReadDeviceExistTest() {
String[] trame = {
"*TS01,351579056605817,003410140618,GPS:3;N46.758156;W71.134046;6;0;0.96,STT:c003;8001,MGR:957975,SAT:43;40;39#" };
EmbeddedChannel channel = new EmbeddedChannel(new DeviceDataHandler());
boolean ok = channel.writeInbound(trame);
assertThat(ok).isTrue();
}
}
Error log
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.amqp.rabbit.core.RabbitTemplate' 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:1654) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
... 44 common frames omitted ##
So,Like I said . if I run just with with Spring-Boot, the RabbitTemplate injection work fine . But , if a run with Junit. I try to add #SpringBootTest
java.lang.IllegalStateException: Configuration error: several statements found from #BootstrapWith for test class [com.AuthenticateHandlerTest]: [# org.springframework.test.context.BootstrapWith (value = class org.springframework.boot.tocon. test.autoconfigure.orm .jpa.DataJpaTestContextBootstrapper), # org.springframework.test.context.BootstrapWith (value = class org.springframework.boot.test.context.SpringBootTestContextBootstrapper)]
at org.springframework.test.context.BootstrapUtils.resolveExplicitTestContextBootstrapper (BootstrapUtils.java:166)
org.springframework.test.context.BootstrapUtils.resolveTestContextBootstrapper (BootstrapUtils.java:127).
Because of #DataJpaTest
I had the same problem and I had to mock the bean that uses RabbitTemplate.
Put this annotation at the top of your test's class name:
#MockBean(RabbitManager.class)
For anyone else, the class will be the one that contains the RabbitTemplate field, i.e.:
#MockBean(MyServiceContainingRabbitTemplate.class)
But that leads me to question why I had to do this. I would like to know :)
The #DataJpaTest annotation in your test uses to scan #Entity, repositories, EntityManager and other necessary beans for working with a database in tests, but this annotation does not load regular #Component beans into the ApplicationContext.
In your test case, you can use the #SpringBootTest annotation instead of the #DataJpaTest to load the entire application context.

WebSocketConfigurer and #Scheduled() are not work well in an application

I can either use websocket that configure with WebSocketConfigurer or use #Scheduled() to schedule task without any problem.
However, java does not compile when i use both of them.
#Scheduled() annotation may crash with
org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.scheduleAtFixedRate()
java.lang.IllegalStateException: Unexpected use of scheduler.
at org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.scheduleAtFixedRate(WebSocketConfigurationSupport.java:123)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleFixedRateTask(ScheduledTaskRegistrar.java:462)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleFixedRateTask(ScheduledTaskRegistrar.java:436)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTasks(ScheduledTaskRegistrar.java:357)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:332)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:280)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:211)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:102)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:353)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:887)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
at com.yeadev.JavaSpringBootJARAngularSeed.JavaSpringBootJarAngularSeedApplication.main(JavaSpringBootJarAngularSeedApplication.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
source code for WebSocketConfiguration
#Slf4j
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// added end point,
// eg. ws://localhost:8080/ws
// added WebSocketHandler to /ws
registry.addHandler(new WebSocketHandler(),"/ws");
log.info("added handler for WebSocket.");
}
}
source code for ScheduledTasks
#Slf4j
#Component
public class ScheduledTasks {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// #Scheduled() annotation can not use with WebSocketConfigurer
#Scheduled(fixedRate=1000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
}
i use Spring Boot 2.0.0.RELEASE
I ran into the same issue today. It looks as if like Spring web socket creates its own taskScheduler bean which doesn't implement any of the normal operations - it just throws IllegalStateException. This bean is then used for all #Scheduled methods. I was able to resolve this by explicitly creating a task scheduler bean in my ApplicationConfig:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
return taskScheduler;
}

Cannot run Unit Tests against REST layer

I cannot unit test the web layer using jhipster (according to spring gs guides):
#RunWith(SpringRunner.class)
#WebMvcTest
public class FlightControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private FlightService flightService;
#Test
public void testCreate() throws Exception {
FlightDto expected = new FlightDto();
ReflectionTestUtils.setField(expected, "id", 1L);
when(flightService.createFlight(any(FlightDto.class))).thenReturn(expected);
FlightDto flightDto = new FlightDto();
flightDto.setNumber("CAI-123400");
this.mockMvc.perform(post("/api/flight")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(flightDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(header().string("Location", endsWith("/api/flight/1")));
}
}
The above Unit test success in case of green-field spring boot project, but fails in case of green-field spring boot-based jhipster project:
When run Unit Test in jhispter project (springboot-with-jhipster) from FlightResource I got:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
at org.springframework.util.Assert.state(Assert.java:70)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOrFindConfigurationClasses(SpringBootTestContextBootstrapper.java:202)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:137)
at org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTestContextBootstrapper.processMergedContextConfiguration(WebMvcTestContextBootstrapper.java:35)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:409)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildDefaultMergedContextConfiguration(AbstractTestContextBootstrapper.java:323)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:277)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:112)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:82)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:120)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:105)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:152)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:143)
at org.springframework.test.context.junit4.SpringRunner.<init>(SpringRunner.java:49)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
I can run the test with this code:
#RunWith(SpringRunner.class)
//#WebMvcTest remove #WebMvcTest
//add SpringBootTest
#SpringBootTest(classes = JhUnittestRestApp.class)
public class FlightResourceTest {
//#Autowired remove anotation
private MockMvc mockMvc;
#MockBean
private FlightService flightService;
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Autowired
private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
#Autowired
private ExceptionTranslator exceptionTranslator;
#Before
public void setup() {
//initialize the bean
MockitoAnnotations.initMocks(this);
final FlightResource flightResource = new FlightResource(flightService);
this.mockMvc = MockMvcBuilders.standaloneSetup(flightResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService())
.setMessageConverters(jacksonMessageConverter).build();
}
#Test
public void testCreate() throws Exception {
FlightDTO expected = new FlightDTO();
ReflectionTestUtils.setField(expected, "id", 1L);
when(flightService.save(any(FlightDTO.class))).thenReturn(expected);
FlightDTO flightDto = new FlightDTO();
flightDto.setNumber("CAI-123400");
//update the url to /api/flights so that the test can pass
this.mockMvc.perform(post("/api/flights")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(flightDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(header().string("Location", endsWith("/api/flights/1")));
}
}
Your FlightControllerTest is working in your springboot-no-jhipster project because the main class of the project is annotated with #SpringBoot According to the documentation of #SpringBoot
The #SpringBootApplication annotation is equivalent to using:
#Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes
Since JHipster needs some more configuration than just the default, JHipster is not using #SpringBootApplication as you can see in your project. This is perfectly OK and works without problems.
On the other hand the error message of your test is saying that it can't detect #SpringBootConfiguration. There are other annotation e.g. #ContextConfiguration or #SpringBootTest that are rocomandet to use for the test. Actually there are some inconsistencies on which annotation in main config class fits with the test annotations see here or here.

Spring Boot Camel Testing

I need to test Camel routes in a Spring Boot Application.
I've the Spring boot main class with all the necessary beans declared in it.
I am using the CamelSpringJUnit4ClassRunner.class.
Added my Spring boot main class in #ContextConfiguration as it contains all the configurations. I don't have a separate configuration class.
I 've autowired CamelContext in my Test class:
#Autowired
CamelContext camelContext;
But the test fails with the error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.apache.camel.CamelContext' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Try to use the CamelSpringBootRunner.class as the runner and add the #SpringBootTest annotation to the test class.
Example from the Camel repository
UPDATE (based on your comment)
If you change your bootstrapper class to SpringBootTestContextBootstrapper then it should work:
#BootstrapWith(SpringBootTestContextBootstrapper.class)
The equivalent configuration as you have but in this case you don't need to add the ContextConfiguration and the BootstrapWith annotation:
#RunWith(CamelSpringBootRunner.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#MockEndpoints("log:*")
#DisableJmx(false)
#SpringBootTest(classes = MyClass.class)
just enable #EnableAutoConfiguration it will work
With Camel 3.1 Spring Boot 2.2.5 and JUnit5, while also setting test application properties:
#CamelSpringBootTest
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestPropertySource(properties = "spring.cloud.consul.enabled=false")
public class CamelRouteTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private CamelContext camelContext;
#EndpointInject("mock:bean:userService")
private MockEndpoint mockUserService;
private User user;
#BeforeEach
public void setUp() throws Exception {
AdviceWithRouteBuilder.adviceWith(camelContext, "getUsersRoute", a -> {
a.mockEndpointsAndSkip("bean:userService*");
});
user = new User();
user.setId(1);
user.setName("Jane");
mockUserService.returnReplyBody(constant(new User[] {user}));
}
#Test
public void callsRestWithMock() {
ResponseEntity<User[]> response = restTemplate.getForEntity("/rest/users", User[].class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
User[] s = response.getBody();
assertThat(s).contains(user);
}
#Test
public void callsDirectRouteWithMock() throws Exception {
User[] users = DefaultFluentProducerTemplate.on(camelContext)
.to("direct:getusers")
.request(User[].class);
assertThat(users).contains(user);
}
#Test
public void camelStarts() {
assertEquals(ServiceStatus.Started, camelContext.getStatus());
assertThat(camelContext.getRoutes()).hasSizeGreaterThan(0);
}
}
Assuming a RouteBuilder:
#Component
public class CamelRouter extends RouteBuilder {
#Value("${server.port}")
private int serverPort;
#Override
public void configure() throws Exception {
restConfiguration()
.contextPath("/rest")
.component("servlet")
.apiContextPath("/api-doc")
.port(serverPort)
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true");
rest("/users")
.consumes("application/json")
.produces("application/json")
.get()
.outType(User[].class).to("direct:getusers");
from("direct:getusers").routeId("getUsersRoute")
.log("Get users")
.to("bean:userService?method=listUsers");
}
}
and application.yml:
camel:
component:
servlet:
mapping:
context-path: /rest/*
springboot:
name: MyCamel

spring test fails on mockServletContext unsupportedOperation

I have a set of Integration Tests running for my Spring-Boot 1.3 app. But I had to add the following to get my maximum sessions working:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ServletContextAware {
...
#Override
public void setServletContext(ServletContext servletContext) {
servletContext.getSessionCookieConfig().setHttpOnly(true);
// causes an ApplicationEvent to be published to the Spring ApplicationContext every time a HttpSession commences or terminates
servletContext.addListener(new HttpSessionEventPublisher());
}
...
}
Now when I run my tests, I get the following:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig' defined in file [/Users/davidclark/projects/edmtotal/build/classes/main/com/edelweissco/dental/configuration/WebSecurityConfig.class]: Initialization of bean failed; nested exception is java.lang.UnsupportedOperationException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
...
Caused by: java.lang.UnsupportedOperationException
at org.springframework.mock.web.MockServletContext.addListener(MockServletContext.java:675)
at com.edelweissco.dental.configuration.WebSecurityConfig.setServletContext(WebSecurityConfig.java:123)
...
Here is an example test class (but they all fall with the same exception):
#Transactional
public class ConfigurationSettingsTest extends BaseSpecification {
#Autowired
private ConfigurationSettings configurationSettings;
#Autowired
ConfigurableApplicationContext context
...
}
where BaseSpecification is:
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#WebAppConfiguration
public class BaseSpecification extends Specification {
#Value('${local.server.port}')
private int serverPort;
def setup() {
RestAssured.port = serverPort;
}
}
It would seem that now when I run my integration tests, a MockServlet is being applied here, and it doesn't support. this feature. When debugging, I see that a SpringBootMockServletContext is trying to be set in setServletContext, and that is where the exception is.
I will post my answer in case anyone else runs into this. The problem was in my BaseSpecification. I added the #WebAppConfiguration and #IntegrationTest to it, and removed #IntegrationTest off the individual integration tests. Apparently this will actually create the ServletContext the way it should be.
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#WebAppConfiguration
#IntegrationTest
public class BaseSpecification extends Specification {

Resources