I am trying to write a test case for rabbitMq listener. I tried using spring-rabbit-test and got the below error while running tests:
Error starting ApplicationContext. To display the conditions report
re-run your application with 'debug' enabled. 2018-03-06 17:10:50.113
ERROR 14239 --- [ main] o.s.boot.SpringApplication
: Application run failed
java.lang.IllegalStateException: Another endpoint is already
registered with id 'response_queue'
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry.registerListenerContainer(RabbitListenerEndpointRegistry.java:151)
~[spring-rabbit-2.0.2.RELEASE.jar:2.0.2.RELEASE]
I was following [https://docs.spring.io/spring-amqp/reference/htmlsingle/#testing] and in the example they have shared it does not have an #Component for the listeners in ideal case that will be a component.
Now my test class also tries to get the listener resultin in the error mentioned above.
Can someone help me?
Test configuration
#Configuration
#RabbitListenerTest
public class RabbitMQTestConfig {
#Autowired
MyListener myListener;
}
Test
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest
public class RabbitMQTest {
#Rule
public BrokerRunning brokerRunning = BrokerRunning.isRunning();
#Autowired
private RabbitListenerTestHarness harness;
#Test
public void testMyListener() {
}
}
Listener
#Component
public class MyListener {
#Autowired
MyService myService;
#RabbitListener(id = "response_queue", queues = "response")
public void processOrder(SomeResponse someResponse) {
myService.process(someResponse);
}
}
Yeah... You will need to share your project with us. It's simple variant to let us to reproduce and play to determine the reason.
Right now I can't reproduce it with very simple Spring Boot app:
#SpringBootApplication
public class So49129095Application {
public static void main(String[] args) {
SpringApplication.run(So49129095Application.class, args);
}
}
#Component
public class MyListener {
#RabbitListener(id = "response_queue", queuesToDeclare = #Queue("response"))
public void processOrder(Object payload) {
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {So49129095Application.class, So49129095ApplicationTests.RabbitMQTestConfig.class})
public class So49129095ApplicationTests {
#Rule
public BrokerRunning brokerRunning = BrokerRunning.isRunning();
#Autowired
private RabbitListenerTestHarness harness;
#Test
public void testMyListener() {
}
#Configuration
#RabbitListenerTest
public static class RabbitMQTestConfig {
#Autowired
MyListener myListener;
}
}
Seems for me I didn't miss any of your points how to configure that all together.
This happens when you register 2 listeners with same id.
Most likely you had a class with listener defined and then you manually created another bean (possibly mocked) of the same class.
Related
Trying to register beans dynamically via SpringApplicationBuilder class and it's working when running the app, but when trying to execute the test and trying to verify that the beans are defined in the context, they fail for the dynamic bean. Feel like I have to use another "magical" annotation for the tests for them to properly load the dynamic beans.
This is the code used and if you run the tests you will see that both cases will fail. BarService will fail also because FooService is registered dynamically via builder, but if you would remove the dependency it will pass the BarService test.
SpringApp.java
class FooService {
}
#Component
class BarService {
private final FooService fooService;
BarService(FooService fooService) {
this.fooService = fooService;
}
}
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers((ApplicationContextInitializer<GenericApplicationContext>) context -> {
context.registerBean(FooService.class);
})
.run(args);
}
}
SpringAppTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class)
public class SpringAppTest {
#Autowired
ApplicationContext context;
#Test
public void barService() {
Assert.assertNotNull("The barService should not be null", context.getBean(BarService.class));
}
#Test
public void contextLoads() {
Assert.assertNotNull("The fooService should not be null", context.getBean(FooService.class));
}
}
First solution
The main error here is that the test does not have the initalization logic that the main method has. Solution is to extract the logic from the initializers method
class MyInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
System.out.println("Called initialize");
context.registerBean(FooService.class);
}
}
and use it in main
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers(new MyInitializer())
.run(args);
}
and then use the MyInitializer in the test file through #ConextConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class, initializers = MyInitializer.class)
public class SpringAppTest {
// ...
}
Second (better) solution
Now, this can be cumbersome as we need to reference this initializer in every test, but there is an even better solution. We can create a specific Spring file resources/META-INF/spring.factories and put inside of it a reference to the initializer:
org.springframework.context.ApplicationContextInitializer=com.acme.orders.MyInitializer
After that, we can simplify both the main method
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
SpringApplication.run(SpringApp.class, args);
}
}
and the tests, so that they don't need to always import the initializer.
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringAppTest {
// ...
}
Now both the main run process and the tests will have access to all the beans.
I'm working on a project using spring boot 2.1.1.RELEASE with junit 4.
This is a command line application, that relies on a CommandLineRunner as "main".
The problem is I need to write a unit test that use some #Autowired stuff
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ExcludeCommandLineRunner.class)
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
value = CommandLineRunner.class))
#ContextConfiguration(classes = ExcludeCommandLineRunner.class)
public class MyTest {
#Autowired
MyService myService;
#Test
public void foo() {
assertEquals(3, this.myService.sum(1, 2));
}
}
#Configuration
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
value = CommandLineRunner.class))
#EnableAutoConfiguration
public class ExcludeCommandLineRunner {
}
but there is no way for me to avoid the fact that the CommandLineRunner is called... how can I do it?
Depending on how you configured your project, you can rely on Profile to skip your CommandLineRunner. Declare a bean CommandLineRunner with a #Profile("!test") and configure your test class to start the test profile.
Here is a sample that works:
#SpringBootApplication
public class SkipCommandLineRunner {
public static void main(String[] args) {
System.setProperty("spring.config.name", "skipcommandlinerunner");
SpringApplication.run(SkipCommandLineRunner.class);
}
#Bean
#Profile("!test")
public CommandLineRunner commandLineRunner() {
return args -> {
System.out.println("I am being called");
};
}
}
#SpringBootTest
#ActiveProfiles("test")
class SkipCommandLineRunnerTest {
#Test
void test() {
System.out.println("Test is here");
}
}
2020-02-14 19:38:29.525 INFO 41437 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#30e143ff, org.springframework.security.web.context.SecurityContextPersistenceFilter#5b59c3d, org.springframework.security.web.header.HeaderWriterFilter#7fd2a67a, org.springframework.security.web.csrf.CsrfFilter#779b4f9c, org.springframework.security.web.authentication.logout.LogoutFilter#484302ee, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#f0c1ae1, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#252d8df6, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter#452ec287, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#410f53b2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#46188a89, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#37fca349, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#41404aa2, org.springframework.security.web.session.SessionManagementFilter#3c3cd13a, org.springframework.security.web.access.ExceptionTranslationFilter#5cb8580, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#4d174189]
2020-02-14 19:38:29.586 INFO 41437 --- [ main] c.z.s.c.SkipCommandLineRunnerTest : Started SkipCommandLineRunnerTest in 3.22 seconds (JVM running for 4.231)
Test is here
You don't see the other I am being called, which shows that the CommandLineRunner is excluded.
Hope it helps
Within #ContextConfiguration you defined your test context configuration to be loaded from ExcludeCommandLineRunner by Spring TestContext, therefore it will be executed.
#ContextConfiguration(classes = ExcludeCommandLineRunner.class)
Also #SpringBootTest annotation will search for a main configuration class (one with #SpringBootApplication (because it is in turn meta-annotated with #SpringBootConfiguration)) and use that to start a Spring application context. In your example, you explicitly defined which class to use for application context bootstrap.
#SpringBootTest(classes = ExcludeCommandLineRunner.class)
You should use one of the above annotations.
Solution : a) Specify other class(es) in #ContextConfiguration or b) include inner static class annotated with #Configuration within MyTest class, which then will be used to load test context. In any case you need to remove #SpringBootTest annotation.
#RunWith(SpringRunner.class)
public class MyTest {
#Autowired
MyService myService;
#Test
public void foo() {
assertEquals(3, this.myService.sum(1, 2));
}
#Configuration
public static class TestContextConfiguration {
// define beans (for example MyService) here
}
}
I want to create a JUnit test for this private method:
#Component
public class ReportingProcessor {
#EventListener
private void collectEnvironmentData(ContextRefreshedEvent event) {
}
}
I tried this:
#ContextConfiguration
#SpringBootTest
public class ReportingTest {
#Autowired
ReportingProcessor reportingProcessor;
#Test
public void reportingTest() throws Exception {
ContextRefreshedEvent contextRefreshedEvent = PowerMockito.mock(ContextRefreshedEvent.class);
Whitebox.invokeMethod(reportingProcessor, "collectEnvironmentData", contextRefreshedEvent);
}
}
When I run the code I get:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Do you know ho I can fix this issue?
If you don't have any class annotated with #SpringBootApplication and the known main() method, you need to target your component class on the #SpringBootTest annotation.
Usually I do this when I'm building libraries and for some specific scenario I need to have the spring context to unit test them.
Just add this to your code:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ReportingProcessor.class)
public class ReportingTest {
...
Just did it and the test is running.
Edit: Don't know what are you trying to test exactly, just wanted to show how can you fix the error you are getting.
You should use the #RunWith with your #SpringBootTest:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ReportingTest {
#Autowired
ReportingProcessor reportingProcessor;
#Test
public void reportingTest() throws Exception {
ContextRefreshedEvent contextRefreshedEvent = PowerMockito.mock(ContextRefreshedEvent.class);
Whitebox.invokeMethod(reportingProcessor, "collectEnvironmentData", contextRefreshedEvent);
}
}
I am new to spring boot and i am trying to test a very simple class. But when i run the testMe() below I get exception below
java.lang.NullPointerException
at MyTest.testMe(MyTest.java:25)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
My understanding is when the context is loaded all the beans are initialized and object HelloWorld is created and are autowired in MyTest call. But helloWorld object is null at line helloWorld.printHelloWorld();
I need assistance here to understand what is missing.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {AppConfigTest.class})
public class MyTest {
#Mock
#Autowired
private Message myMessage;
#Autowired
private HelloWorld helloWorld;
#Test
public void testMe(){
helloWorld.printHelloWorld();
}
}
#Configuration
public class AppConfigTest {
#Bean
public HelloWorld helloWorld() {
return new HelloWorldImpl();
}
#Bean
public Message getMessage(){
return new Message("Hello");
}
}
public interface HelloWorld {
void printHelloWorld();
}
public class HelloWorldImpl implements HelloWorld {
#Autowired
Message myMessage;
#Override
public void printHelloWorld() {
System.out.println("Hello : " + myMessage.msg);
}
}
public class Message {
String msg;
Message(String message){
this.msg = message;
}
}
You're running your tests with a runner that's not Spring-aware, so no wiring is happening. Look at the Spring Boot testing documentation, all their examples use #RunWith(SpringRunner.class). To mock a bean, annotate it with #MockBean, not #Mock. Make sure that the spring-boot-starter-test is included in your POM.
my application normally works fine, but when I run tests, or build application by maven, application is shutting down due tests with errors java.lang.NullPointerException. I debugged it and find out my that my beans in service layer are not Autowired and they are null.
Here is my class with tests:
public class CompanyServiceSimpleTest {
private CompanyService companyService;
#Before
public void setUp() {
companyService = new CompanyServiceImpl();
}
// Here is sample test
#Test
public void testNumberOfCompanies() {
Assert.assertEquals(2, companyService.findAll().size());
}
}
companyService is initialized, but beans in it not. Here is CompanyServiceImpl:
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyRepository companyRepository; // is null
#Autowired
private NotificationService notificationService; // is null
#Override
public List<CompanyDto> findAll() {
List<CompanyEntity> entities = companyRepository.find(0, Integer.MAX_VALUE);
return entities.stream().map(Translations.COMPANY_DOMAIN_TO_DTO).collect(Collectors.toList());
}
// ... some other functions
}
So when is called companyRepository.find() applications crashes. Here is repository class:
#Repository
#Profile("inMemory")
public class CompanyInMemoryRepository implements CompanyRepository {
private final List<CompanyEntity> DATA = new ArrayList<>();
private AtomicLong idGenerator = new AtomicLong(3);
#Override
public List<CompanyEntity> find(int offset, int limit) {
return DATA.subList(offset, Math.min(offset+limit, DATA.size()));
}
// ... some others functions
}
I have set up profile for that service but I had that VM options in Idea:
-Dspring.profiles.active=develpment,inMemory
So it should works.
To make autowiring work it has to be a Spring integration test. You have to anotate your test class with:
#RunWith(SpringJUnit4ClassRunner.class) and #ContextConfiguration(classes = {MyApplicationConfig.class})
If it is a Spring Boot app e.g.:
#RunWith(SpringJUnit4ClassRunner.class) and #SpringBootTest(classes = {MyApp.class, MyApplicationConfig.class}, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
More on this topic: http://www.baeldung.com/integration-testing-in-spring and http://www.baeldung.com/spring-boot-testing
You are not configuring Spring in your TestClasses, so it's impossible to inject anything...
Try configurin your class with
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "path to your config xml" })
A little example:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:config/applicationContext.xml"})
public class MyTest {
#Autowired
private MyClass myInjectedClass;
#Test
public void someTest() {
assertNotNull(myInjectedClass);
}
}