SpringBoot and JUnit - Testing Service class - Failed to load Application Context - java

I'm trying to run a unit test on a service class in a Spring Boot Application
i would like to try this test
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class) //my #SpringBootApplication class
public class UserServiceTest { //i'm testing my UserService implementation
#TestConfiguration
static class UserServiceContextConfiguration {
#Bean
public IUserService service() {
return new UserService();
}
}
#Autowired
private IUserService service;
#MockBean
private UserRepository repository;
#Before
public void setUp() {
User me = new User();
me.setEmail("admin#admin.com");
Mockito.when(repository.findByEmail(me.getEmail())).thenReturn(me);
}
#Test
public void whenValidEmail_thenFindUser() {
String email = "admin#admin.com";
User found = service.findByEmail(email);
assertThat(found.getEmail()).isEqualTo(email);
}
}
But when launching the test i get this exception
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.myapp.service.UserServiceTest': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.myapp.service.interfaces.IUserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Maybe it is not clear to me but #TestConfiguration should allow me to define my beans from the application to use them in the tests and #SpringBootTest should load all the application context from the app for the test environment...

By providing classes=Application.class you turned off automatic scanning of inner configuration classes.
Either remove the explicit classes parameter - SpringRunner will search for SpringBootApplication annotated class in current packages and parent packages and also search for inner configuration classes,
or add this to your #SpringBootTest
#SpringBootTest(classes= {Application.class, UserServiceContextConfiguration.class })

Related

#Component not being read in Spring test

I created an Integration test to test the new feature I just added but the Spring wiring is not working. The unit tests all work and the existing Spring integration tests still work but I am unable to Autowire my new class
Here is the error message –
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.xxx.xxx.etc.MyNewClassTest’: Unsatisfied dependency expressed through field 'sut'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xxx.xxx.etc.MyNewClass ' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
The new class –
#Slf4j
#Component
public class MyNewClass extends AbstractRetryJob<Event> {
My test -
#ExtendWith(SpringExtension.class)
class MyNewClassTest {
#Autowired private MyNewClass sut;
Any idea on what is going wrong?
Adding #ExtendWith(SpringExtension.class) is not enough to create the Spring context. You need to add #SpringBootTest to your MyNewClassTest. Per the appropriate comment, you can drop #ExtendWith(SpringExtension.class)
#SpringBootTest
class MyNewClassTest {
#Autowired private MyNewClass sut;
}

How to load ServerHttpSecurity in #SpringBootTest?

I want to create a #SpringBootTest that makes use of my full configuration structure.
Problem: I'm creating an #Bean SecurityWebFilterChain that requires a ServerHttpSecurity, which is somehow missing in a test:
#SpringBootApplication
public class MainApp { ... }
//for simple testing I started with anonymous auth
#Configuration
public class ReactiveSecurityConfiguration {
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http.anonymous()
.and().csrf().disable()
.build();
}
}
#SpringBootTest
public class TestClass {
#Test
public void test() {
}
}
Result:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'securityWebFilterChain' defined in class path resource [ReactiveSecurityConfiguration.class]:
Unsatisfied dependency expressed through method 'securityWebFilterChain' parameter 0;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.security.config.web.server.ServerHttpSecurity' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Update
I discovered if I add the following annotations to my Test Class, the testmethod works without errors. But is that intentional?
#EnableWebFlux
#EnableWebFluxSecurity
#SpringBootTest
public class TestClass { }
I'm not certain how your project is arranged, but no, it's not intentional. You can take a look at Spring Security's Reactive Sample that demonstrates this.
It may be that your TestClass is not finding the #SpringBootConfiguration annotation attached to MainApp.
I have a spring-boot-starter-web dependency pulled somewhere in the classpath. Removing it resolved the problem.
If both web and webflux dependency should be kept, it's still possible to run a test in reactive only, with:
#SpringBootTest(properties = "spring.main.web-application-type=REACTIVE")
class MyWebFluxTests {
// ...
}

Exclude EurekaClient Bean from Application Context in SpringBootApplicationTest

I'm trying to write a integration test against my Spring Data API with the following test configuration.
eureka:
client:
enabled: false
[..] # No other configuration part that affects discovery/eureka client
This is my test class
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
#Transactional
class FooAPITest {
#Test
void contextLoads() {
}
}
However I have a component which injects the EurekaClient to get a service instance from it
#Component
public class ServiceClient {
#Autowired
public ServiceClient(#Qualifier("eurekaClient") EurekaClient eurekaClient) {
URI serviceUri = URI.create(eurekaClient.getNextServerFromEureka("service", false).getHomePageUrl());
}
}
So as of this service my application is not able to load the ApplicationContext.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.netflix.discovery.EurekaClient' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value="eurekaClient")}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 81 more
What I've tried so far
I thought about setting up a custom ContextConfiguration to exclude the ServiceClient as it is not needed in the test class. However I need to include a Configuration File which autowires the EntityManager but when I use #SpringBootApplication(classes = {Configuration.class}) the EntityManager can not be injected. This Configuration looks like that:
#Configuration
class Configuration {
#Autowired
EntityManager entityManager;
}
This produces the same error but with EntityManager Bean:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' 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:1695)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 81 more
Current Workaround
Currently I'm avoiding the problem by mocking the ServiceClient but I want to get rid of that code smell.
#MockBean
ServiceClient serviceClient;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(FooAPITest.class);
}
Another workaround would be to mark the Injected Beans as not required but I don't find that practicable only to make the tests work.
What is a proper way to solve this problem?
You can try mocking the EurekaClient in your test:
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
#Transactional
class FooAPITest {
#MockBean
private EurekaClient eurekaClient;
#Test
void contextLoads() {
}
}
This will create the EurekaClient as a mocked bean in the ApplicationContext to be injected into your service.
If you have other tests that initialize the Spring ApplicationContext, you can create a separate configuration class within the application package to be scanned (using #ConditionalOnMissingBean annotation to cover all bases):
#Configuration
public class MockEurekaConfiguration {
#Bean
#ConditionalOnMissingBean
public EurekaClient eurekaClient() {
return Mockito.mock(EurekaClient.class);
}
}

(Context) Problems with #RestController testing with JUnit4 and Spring

I have 3 separate projects, bound by pom.xml
main myapp folder with pom.xml having modules section:
<modules>
<module>myapp-dao</module>
<module>myapp-webapp</module>
<module>myapp-configuration</module>
</modules>
3 projects inside myapp folder (each having its own pom.xml)
myapp-dao
myapp-configuration
myapp-webapp
I am writing a JUnit test for a #RestController class in the myapp-webapp module (don't worry about the #Test contents, it is just a skeleton, it will be expanded when I will be able to run the test):
#RunWith(SpringRunner.class)
#AutoConfigureMockMvc
#WebMvcTest(ContentController.class)
public class ContentControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private ContentService contentService;
#MockBean
private HyperlinkReferenceService hyperlinkReferenceService;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
given(contentService.findContentUsedAsTemplateIn(1, 0)).willReturn(null);
mvc.perform(get("/portal/content/1/references/usedAsTemplateIn").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
When I am trying to run the test, I get:
java.lang.IllegalStateException: Failed to load ApplicationContext
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.pro4people.msg.ServletInitializer]; nested exception is java.io.FileNotFoundException: class path resource [myapp.properties] cannot be opened because it does not exist
I have resolved this problem by copying myapp-configuration/target/myapp.properties to myapp-webapp/src/main/test/resources/myapp.properties
This solved the above problem, but another problem came out:
java.lang.IllegalStateException: Failed to load ApplicationContext
Description:
Field userRepository in com.company.myapp.service.UserServiceImpl required a bean of type 'com.company.myapp.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.company.myapp.repository.UserRepository' in your configuration.
But really, I am not even using the UserRepository in this test, so I presume that there is a Spring context problem somewhere around. The application is normally built to a war and deployed to Tomcat, and only in its wared state everything is correctly injected and bound.
How can I omit this problem? Whatever I am trying to do, the test is raising the spring context, and when I remove #WebMvcTest(ContentController.class), I get:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.company.myapp.controller.portal.json.ContentControllerTest': Unsatisfied dependency expressed through field 'mvc'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 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)}
Which seems to be obvious.
Use Context configuration on top of your controller test class.
#ContextConfiguration(classes = {ContentController.class})
Below code works for unit test for controller.
#RunWith(MockitoJUnitRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = {ContentController.class})
public class ContentController {
private MockMvc mockMvc;
#InjectMocks
private ContentController contentController;
#Mock
private ContentService contentService;
....
/**
* Configure the mockMvc with Controller.
*/
#Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(contentController).build();
}
#Test
your test here() {
//use Mockito.when(statement).thenReturn(value);
//rest remain same using mockMVC
mockMvc.perform(...)
}

Spring Boot 1.4 - TestRestTemplate Unsatisfied dependency exception

Have a very light Spring Boot 1.4 project, generated from start.spring.io.
Trying to run an intergration test for #RestController with #RequestBody using TestRestTemplate, but there's no success because of a startup exception.
The only configuration class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Configuration file application.properties has almost nothing except of security.ignored=/** for the test purposes.
The test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#DataJpaTest
public class MyControllerTest {
private Logger log = Logger.getLogger(getClass());
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private TestEntityManager entityManager;
#Before
public void init() {
log.info("Initializing...");
}
#Test
public void addTest() throws Exception {
log.info("MyController add test starting...");
// restTemplate usage
log.info("MyController add test passed");
}
}
... but during the test startup I get the following exception:
ERROR 6504 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener#5444f1c3] to prepare test instance [com.myproject.controllers.MyControllerTest#5d2bc446]
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.myproject.controllers.MyControllerTest': Unsatisfied dependency expressed through field 'restTemplate': No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate] found for dependency [org.springframework.boot.test.web.client.TestRestTemplate]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate] found for dependency [org.springframework.boot.test.web.client.TestRestTemplate]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
According to the doc it's not required to configure TestRestTemplate anywhere. However, I've added the latest Apache Http Client to the classpath as it recommended.
What have I missed?
You are specifying #DataJpaTest which tells Spring to exclude any wiring of the web context for the tests. As such there is no TestRestTemplate created. Read this blog for more details around testing slices of your application: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4#testing-application-slices
I had a similar problem running the main class on Eclipse using Serenity BDD tests with a spring-boot. It starts to fail after I have added the spring-boot-test-autoconfigure test dependency. That happens because Eclipse put everything in just one classloader. In order to fix this error, I have created one configuration class overriding the default behavior of the spring-boot. This code was based in one spring class (the scope not is public) SpringBootTestContextCustomizer.TestRestTemplateFactory
#TestConfiguration
public class TestConfig {
// Overriding Default Spring Boot TestRestTemplate to allow
// execute the main method from Eclipse (mixed Classloader)
#Bean
#Primary
public TestRestTemplate testRestTemplate(ApplicationContext context, RestTemplateBuilder templateBuilder) {
final AbstractConfigurableEmbeddedServletContainer container = context.getBean(AbstractConfigurableEmbeddedServletContainer.class);
final boolean sslEnabled = container.getSsl() != null && container.getSsl().isEnabled();
final TestRestTemplate template = new TestRestTemplate(templateBuilder.build(), null, null, sslEnabled? new HttpClientOption[]{}: new HttpClientOption[]{HttpClientOption.SSL});
template.setUriTemplateHandler(new LocalHostUriTemplateHandler(context.getEnvironment(), sslEnabled ? "https" : "http"));
return template;
}
}

Categories