Spring JUnit Test not loading full Application Context - java

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

Related

avoid call CommandLineRunner in junit4

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
}
}

Unit Test a configuration class not working in Multi Maven Spring Boot project

I am working on multi module maven project with SpringBoot framework.
The project is divided in 4 modules: rest module,service module,repository module, domain module.
I am trying to write a unit test for a configuration class in java which is located in the service module.
I have simplified the case to get rid of business logic complexity. The configuration class is like below:
#Configuration
#ConfigurationProperties(prefix = "x.y.feature", ignoreInvalidFields = false)
public class FeatureConfig {
private String featureUrl;
public String getFeatureUrl() {
return featureUrl;
}
public void setFeatureUrl(String FeatureUrl) {
this.featureUrl = featureUrl;
}
}
The properties file is application.properties.
x.y.feature.featureUrl=featureUrl
And below is the unit test that is not working.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { FeatureConfigTest.class })
public class FeatureConfigTest {
#Autowired
private FeatureConfig featureConfig;
#Test
public void testgetFeatureUrl() {
String expected ="featureUrl";
assertEquals(expected,featureConfig.getFeatureUrl());
}
}
When i run the unit test , it throws the exception below:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
The problem is that you are trying to use object of FeatureConfig
from the application context. You haven't initialized the application context.
When you use the #Autowire annotation, you are mapping field 'featureConfig'
with instance of type FeatureConfig class located in the application context.
To bypass this error you need initialize the application context.
First thing , create static class inside the test class that will help us not to load the
whole application context. Because for this test case you don't need to load the whole application.
First thing , create static class inside the test class that will help us not to load the
whole application context. Because for this test case you don't need to load the whole application.
#EnableConfigurationProperties(FeatureConfig.class)
public static class TestConfiguration {
}
After that you start the application context , but with passing as configuration the static class
you created. This is done not to load the whole application context.
#SpringBootTest(classes = { FeatureConfigTest.TestConfiguration class })
Copy and paste the changes below in your test class and everything should work ok.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { FeatureConfigTest.TestConfiguration class })
public class FeatureConfigTest {
#Autowired
private FeatureConfig featureConfig;
#Test
public void testgetFeatureUrl() {
String expected ="featureUrl";
assertEquals(expected,featureConfig.getFeatureUrl());
}
#EnableConfigurationProperties(FeatureConfig.class)
public static class TestConfiguration {
}
}

Test uses internal ContextConfiguration of other test

I created a new test in my Project. For this one I used #ContextConfiguration with an internal Configuration class in the same class as the Test. But now my other tests are failing because they are using the configuration of the new test.
How is this possible, I thought it is not possible to use a configuration inside of a test class from outside.
When I remove the internal configuration from the new test every other test works fine again.
#DataJpaTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#ContextConfiguration(classes = EventServiceTest.Config.class)
class EventServiceTest {
#Configuration
#Import({WorkingTimeConfig.class,
PartnerConfig.class,
ProjectConfig.class,
UserConfig.class,
AccountGroupConfig.class,
LanguageConfig.class,
CountryConfig.class,
EventConfig.class,
LanguageConfig.class})
static class Config {
#SuppressWarnings("unused")
#MockBean(reset = MockReset.BEFORE)
private UserAttendanceBoard userAttendanceBoard;
#Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
public ImpersonateProperties impersonateProperties() {
return new ImpersonateProperties();
}
}
...
}
Now this Test is not working:
#Import(MailSenderAutoConfiguration.class)
#DataJpaTest
#Transactional
public class ServiceTimeEntryServiceTest {
private ServiceTimeService serviceTimeService;
private ServiceTimeEntryRepository repository;
#Autowired
public ServiceTimeEntryServiceTest(ServiceTimeService serviceTimeService, ServiceTimeEntryRepository repository) {
this.serviceTimeService = serviceTimeService;
this.repository = repository;
}
#Test
void getAllByAccountId() {...}
This error is thrown if I try to start my old tests:
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'passwordEncoder' defined in class path resource [de/hlservices/timetracking/api/business/event/EventServiceTest$Config.class]: Cannot register bean definition
Thanks for your help :)
As Maciej Kowalski pointed out, this issue is probably related to a #ComponentScan annotation.
If you're using it, consider adding an excludeFilter to ensure you only get what you really want. You might want to exclude other configuration classes to be found by your #ComponentScan annotation:
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ANNOTATION,
value = Configuration.class)
})
Btw: I really recommend using IntelliJ IDEA as IDE because of the awesome spring support.
You can lookup what beans/components are found by your scan just by clicking on the green icon left of your code (line:9) :
This makes debugging scanning issues way easier.
I had the same issue in my project and it was due to the fact that the #ComponentScan was picking up also that class due to the #Configuration annotation.
Everything worked fine when I removed that annotation and thus making the component scan to omit it. So you can have just like that:
#Import({WorkingTimeConfig.class,
PartnerConfig.class,
ProjectConfig.class,
UserConfig.class,
AccountGroupConfig.class,
LanguageConfig.class,
CountryConfig.class,
EventConfig.class,
LanguageConfig.class})
static class Config {
Removing #Configuration annotation did not prevent #ContextConfiguration(classes = EventServiceTest.Config.class) config to pick it up anyway.

Getting "At least one JPA metamodel must be present" with #WebMvcTest

I'm fairly new to Spring, trying to do some basic integration tests for a #Controller.
#RunWith(SpringRunner.class)
#WebMvcTest(DemoController.class)
public class DemoControllerIntegrationTests {
#Autowired
private MockMvc mvc;
#MockBean
private DemoService demoService;
#Test
public void index_shouldBeSuccessful() throws Exception {
mvc.perform(get("/home").accept(MediaType.TEXT_HTML)).andExpect(status().isOk());
}
}
but I'm getting
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
Unlike most people posting this error, I don't want to use JPA for this. Am I trying to use #WebMvcTest incorrectly? How can I track down the Spring magic that's inviting JPA to this party?
Remove any #EnableJpaRepositories or #EntityScan from your SpringBootApplication class instead do this:
package com.tdk;
#SpringBootApplication
#Import({ApplicationConfig.class })
public class TdkApplication {
public static void main(String[] args) {
SpringApplication.run(TdkApplication.class, args);
}
}
And put it in a separate config class:
package com.tdk.config;
#Configuration
#EnableJpaRepositories(basePackages = "com.tdk.repositories")
#EntityScan(basePackages = "com.tdk.domain")
#EnableTransactionManagement
public class ApplicationConfig {
}
And here the tests:
#RunWith(SpringRunner.class)
#WebAppConfiguration
#WebMvcTest
public class MockMvcTests {
}
I had the same problem. #WebMvcTest looks for a class annotated with #SpringBootApplication (in the same directory or higher up in your app structure if it doesn't find one). You can read how this works # https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests.
If your class annotated with #SpringBootApplication also has #EntityScan /#EnableJpaRepositories this error occurs. Because you have these annotations with #SpringBootApplication and you are mocking the service ( so actually not using any JPA ). I found a workaround which may not be the prettiest, but works for me.
Place this class in your test directory ( the root ). #WebMvcTest will find this class before your actual Application class. In this class you don't have to add #EnableJpaRepositories/#EntityScan.
#SpringBootApplication(scanBasePackageClasses = {
xxx.service.PackageMarker.class,
xxx.web.PackageMarker.class
})
public class Application {
}
And your test will look the same.
#RunWith(SpringRunner.class)
#WebMvcTest
#WithMockUser
public class ControllerIT {
#Autowired
private MockMvc mockMvc;
#MockBean
private Service service;
#Test
public void testName() throws Exception {
// when(service.xxx(any(xxx.class))).thenReturn(xxx);
// mockMvc.perform(post("/api/xxx")...
// some assertions
}
}
Hope this helps!
Alternatively, you can define a custom configuration class inside your test case, including only the controller (plus its dependencies), to force Spring to use this context.
Please note, you'll still have access to MockMvc and other goodness in your test case, if it's WebMvcTest annotated.
#RunWith(SpringRunner.class)
#WebMvcTest(DemoController.class)
public class DemoControllerIntegrationTests {
#Autowired
private MockMvc mvc;
#MockBean
private DemoService demoService;
#Test
public void index_shouldBeSuccessful() throws Exception {
mvc.perform(get("/home").accept(MediaType.TEXT_HTML)).andExpect(status().isOk());
}
#Configuration
#ComponentScan(basePackageClasses = { DemoController.class })
public static class TestConf {}
Add #MockBean(JpaMetamodelMappingContext.class) to above of class DemoControllerIntegrationTests:
#RunWith(SpringRunner.class)
#WebMvcTest(DemoController.class)
#MockBean(JpaMetamodelMappingContext.class)
public class DemoControllerIntegrationTests {
...
}
Because you have not used a database in your test, Spring throws this exception. By mocking JpaMetamodelMappingContext class you will bypass the needed metamodel.
If anyone uses Spring boot and don't want to remove #EntityScan and #EnableJpaRepositories you can remove #WebMvcTest annotation from your test class and add the following instead:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
public class DemoControllerIntegrationTests {
#Autowired
private MockMvc mvc;
//...
}
and you will be able to autowire MockMvc and use it.

XML-less configuration for spring

I have the following configuration bean for a non web app
#Configuration
public class MyBeans {
#Bean
#Scope(value="prototype")
MyObject myObject() {
return new MyObjectImpl();
}
}
On the other side I have my class
public class MyCommand implements Command {
#Autowired
private MyObject myObject;
[...]
}
How can I make myCommand be autowired with the configuration in MyBeans without using XML so I can inject mocks in my other test classes?
Thanks a lot in advance.
With XML-based configuration you'd use the ContextConfiguration annotation. However, the ContextConfiguration annotation doesn't appear to work with Java Config. That means that you have to fall back on configuring your application context in the test initialization.
Assuming JUnit4:
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest{
private ApplicationContext applicationContext;
#Before
public void init(){
this.applicationContext =
new AnnotationConfigApplicationContext(MyBeans.class);
//not necessary if MyBeans defines a bean for MyCommand
//necessary if you need MyCommand - must be annotated #Component
this.applicationContext.scan("package.where.mycommand.is.located");
this.applicationContext.refresh();
//get any beans you need for your tests here
//and set them to private fields
}
#Test
public void fooTest(){
assertTrue(true);
}
}

Categories

Resources