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
}
}
Related
The question seems extremely simple, but strangely enough I didn't find a solution.
My question is about adding/declaring a bean in a SpringBootTest, not overriding one, nor mocking one using mockito.
Here is what I got when trying the simplest implementation of my real need (but it doesn't work):
Some service, bean, and config:
#Value // lombok
public class MyService {
private String name;
}
#Value // lombok
public class MyClass {
private MyService monitoring;
}
#Configuration
public class SomeSpringConfig {
#Bean
public MyClass makeMyClass(MyService monitoring){
return new MyClass(monitoring);
}
}
The test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class })
public class SomeSpringConfigTest {
private String testValue = "testServiceName";
// this bean is not used
#Bean
public MyService monitoringService(){ return new MyService(testValue); }
// thus this bean cannot be constructed using SomeSpringConfig
#Autowired
public MyClass myClass;
#Test
public void theTest(){
assert(myClass.getMonitoring().getName() == testValue);
}
}
Now, if I replace the #Bean public MyService monitoring(){ ... } by #MockBean public MyService monitoring;, it works. I find it strange that I can easily mock a bean, but not simply provide it.
=> So how should I add a bean of my own for one test?
Edit:
I think ThreeDots's answer (create a config test class) is the general recommendation.
However, Danylo's answer (use #ContextConfiguration) fit better to what I asked, i.e. add #Bean directly in the test class.
Spring Test needs to know what configuration you are using (and hence where to scan for beans that it loads). To achieve what you want you have more options, the most basic ones are these two:
Create configuration class outside the test class that includes your bean
#Configuration
public class TestConfig {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
and then add it to to test as configuration class #SpringBootTest(classes = { SomeSpringConfig.class, TestConfig.class })
or
If you only need to use this configuration in this particular test, you can define it in static inner class
public class SomeSpringConfigTest {
#Configuration
static class ContextConfiguration {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
}
this will be automatically recognized and loaded by spring boot test
Simply add the config as
#ContextHierarchy({
#ContextConfiguration(classes = SomeSpringConfig.class)
})
What i am using in this cases is #Import:
#DataJpaTest(showSql = false)
//tests against the real data source defined in properties
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Import(value = {PersistenceConfig.class, CustomDateTimeProvider.class})
class MessageRepositoryTest extends PostgresBaseTest {
....
Here i am using a pre configured "test slice".
In this case a need to add my JpaAuditingConfig.
But why not just adding the other beans as you did with your SomeSpringConfig.class ?:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class, OtherBean.class })
public class SomeSpringConfigTest {
...
Everything listed in test will be injectable directly, all not declared must be added as mocks.
I have a spring boot configuration class like this:
#Configuration
public class ClockConfiguration {
#Bean
public Clock getSystemClock() {
return Clock.systemUTC();
}
}
and I have some integration tests like this:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
}
and tests like this:
public class MiscTests extends AbstractIntegrationTest{
#Test
public void CreateSomethingThatOnlyWorksInThe Morning_ExpectCorrectResponse() {
}
I want to be able to offset the clock bean to run some tests at different times on the day. How do I do this?
NOTE: I see several stack overflow answers similar to this, but I can't get them to work.
Based on other responses, it appears the solution should be something like:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
#Configuration
class MyTestConfiguration {
#Bean
public Clock getSystemClock() {
Clock realClock = Clock.systemDefaultZone();
return Clock.offset(realClock, Duration.ofHours(9));
}
}
}
But nothing happens there. Do I need to #Import something? do I need to #Autowired something?
Thanks!
As you are using Spring Boot you can take advantage of the #MockBean annotation:
#SpringBootTest(classes = MyApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class AbstractIntegrationTest {
#MockBean
private Clock clockMock;
}
Then you can stub public methods of that bean an each of the tests accordingly and uniquely:
#Test
public void CreateSomethingThatOnlyWorksInThe Morning_ExpectCorrectResponse() {
when(clockMock.getTime()).thenReturn(..);
}
As per javadoc of #MockBean:
Any existing single bean of the same type defined in the context will
be replaced by the mock.
it is the #TestConfiguration annotation that you need https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/context/TestConfiguration.html
#RunWith(SpringRunner.class)
public class ClockServiceImplIntegrationTest {
#TestConfiguration
static class TestOverridingClockServiceConfiguration {
#Bean
public ClockService clockService() {
return new ClockServiceImpl();
}
}
#Autowired
private ClockService clockService;
#MockBean
private ClockRepository clockRepository;
// write test cases here
}
In the case you have existing configuration you c
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.
Hi I am trying to so spring junit test cases... and I require my full application context to be loaded. However the junit test does not initialize the full application context.
Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class MongoDbRepositoryTest {
#Value("${spring.datasource.url}")
private String databaseUrl;
#Inject
private ApplicationContext appContext;
#Test
public void testCRUD() {
System.out.println("spring.datasource.url:" + databaseUrl);
showBeansIntialised();
assertEquals(1, 1);
}
private void showBeansIntialised() {
System.out.println("BEEEAAANSSSS");
for (String beanName : appContext.getBeanDefinitionNames()) {
System.out.println(beanName);
}
}
Output:
spring.datasource.url:${spring.datasource.url}
BEEEAAANSSSS
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalPersistenceAnnotationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
Main Application Class Annotations:
#ComponentScan(basePackages = "com.test")
#EnableAutoConfiguration(exclude = { MetricFilterAutoConfiguration.class, MetricRepositoryAutoConfiguration.class })
#EnableMongoRepositories("com.test.repository.mongodb")
#EnableJpaRepositories("com.test.repository.jpa")
#Profile(Constants.SPRING_PROFILE_DEVELOPMENT)
public class Application { ...
Hence it should scan all the spring bean in the package com.test and also load them into the applicationcontext for the Junit testcase. But from the output of the beans initalised it doesnt seem to be doing this.
You need to annotate your test class with #ActiveProfiles as follows; otherwise, your Application configuration class will always be disabled. That's why you currently do not see any of your own beans listed in the ApplicationContext.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#ActiveProfiles(Constants.SPRING_PROFILE_DEVELOPMENT)
public class MongoDbRepositoryTest { /* ... */ }
In addition, Application should be annotated with #Configuration as was mentioned by someone else.
Are you maybe missing an #Configuration annotation on your Application class?
Adding #ActiveProfile in each test class you cant scale better added this in VM options
-Dspring.profiles.active=test
I was wondering if there is a way to reduce the amount of boilerplate that we are currently writing for out integration tests.
The main culprit is ContextConfiguration that we send 7 distinct strings into currently.
One of our tests looks like this (payload code removed):
#ContextConfiguration(locations = {"classpath:properties-config.xml",
"classpath:dataSources-config.xml",
"classpath:dao-config.xml",
"classpath:services-config.xml",
"classpath:ehcache-config.xml",
"classpath:test-config.xml",
"classpath:quartz-services.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
#Category(IntegrationTest.class)
public class TerminalBuntsPDFTest {
#Autowired
private JobService jobService;
#Test
public void testCode() throws SystemException {
assertTrue("Success", true);
}
}
And the specification of what xml files to load takes up a lot of space. We are in a (very slow) process of migrating away from xml towards annotations, but there is a lot of work left to do in that project.
We are using spring 3.2.
The annotation based approach is to create a Spring Configuration Java class like this:
#Configuration("testConfig")
#ImportResource({
"dataSources-config.xml",
"dao-config.xml",
"services-config.xml"
})
public class TestConfiguration {
// TO create a spring managed bean
#Bean
MyBean myBean() {
return new MyBean();
}
}
Then you can annotate your test class like so to load the configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
classes=TestConfiguration.class,
loader=AnnotationConfigContextLoader.class
)
#Category(IntegrationTest.class)
public class TerminalBuntsPDFTest {
This is just a light example that probably won't compile but should get you on the right track
Some relevant docs:
http://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
What about such pattern:
#ContextConfiguration(locations = {"classpath:properties-config.xml",
"classpath:dataSources-config.xml",
"classpath:dao-config.xml",
"classpath:services-config.xml",
"classpath:ehcache-config.xml",
"classpath:test-config.xml",
"classpath:quartz-services.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
#Category(IntegrationTest.class)
public abstract class BaseTest {
}
// ....
public class TerminalBuntsPDFTest extends BaseTest {
#Autowired
private JobService jobService;
#Test
public void testCode() throws SystemException {
assertTrue("Success", true);
}
}
// ....
public class TerminalBuntsPDFTest2 extends BaseTest {}
This will allow you to place configuration only once in parent abstract class.