How autowire a repository in an integration test with Spring Boot? - java

I'm trying to write an integration test but I'm having trouble with autowiring the repository in the test.
I receive this exception:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.observer.media.repository.ArticleRepository]: Specified class is an interface.
Edit:
I added PersistenceConfig.class with #EnableJpaRepositories, code below. This results in Exception org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
I also tried to add Application.class in #SpringBootTest(classes = {} in an catch all attempt but that throws Error creating bean with name 'articleRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class org.observer.media.model.Article
ScraperRunnerIntegrationTest (the config classes only contain beans of domain classes):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {
ApplicationConfig.class,
PersistenceConfig.class,
DeMorgenTestConfig.class,
Article.class,
ScraperRunner.class,
DeMorgenWebsiteScraper.class,
ArticleService.class,
DeMorgenPageScraper.class,
JsoupParser.class,
DeMorgenArticleScraper.class,
GenericArticleScraper.class,
ImageMetaScraper.class,
ArticlePrinter.class,
ArticleRepository.class
})
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}
Repository:
import org.observer.media.model.Article;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface ArticleRepository extends JpaRepository<Article, Long> {
List<Article> findAll();
Article findByHash(String hash);
Article findByHeadline(String headline);
List<Article> findArticleByHeadlineAndCompanyName(String headline, String companyName);
#Query("SELECT CASE WHEN COUNT(a) > 0 THEN true ELSE false END FROM Article a WHERE a.hash = :hash")
boolean existsByHash(#Param("hash") String hash);
}
PersistenceConfig.class:
package org.observer.media.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#Configuration
#EnableJpaRepositories("org.observer.media.repository")
public class PersistenceConfig {
}

You need to provide only the classes, annotated as #Configuration to #SpringBootTest.
I have modified the original example from here to use the #SpringBootTest annotation.
So the following configuration works:
#Configuration
#ComponentScan("hello")
public class AppConfig {
}
Note the #ComponentScan annotation.
And then in my test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={AppConfig.class})
public class CustomerRepositoryTests {
#Autowired
private CustomerRepository customerRepository;
}
And it did a trick. You can try to do the same in your example.

I had the same exception org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Specified class is an interface on my integration test.
I had resolved without using a PersistenceConfig.class but changed only the annotaion used in my integration class that was wrong. I have used the following annotation:
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
because I didn't want use a in-memory database.
Your code could be the following:
#ComponentScan(basePackages = {"org.observer"})
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class ScraperRunnerIntegrationTest {
private final static Article EXPECTED_ARTICLE_1 = anArticle().withHeadline("headline1").build();
private final static Article EXPECTED_ARTICLE_2 = anArticle().withHeadline("headline2").build();
#Autowired
private ScraperRunner scraperRunner;
#Autowired
private DeMorgenWebsiteScraper deMorgenWebsiteScraper;
#Autowired
private ArticleRepository articleRepository;
#Test
public void run() {
scraperRunner.run(deMorgenWebsiteScraper);
assertThat(articleRepository.findAll()).containsOnly(EXPECTED_ARTICLE_1, EXPECTED_ARTICLE_2);
}

Maybe you can try #AutoConfigureDataJpa or #AutoConfigureXxx annotations.

Related

Java / Spring: Cannot test Caching in Unit Test

In my Java application I am trying to test the following service method with a Unit Test:
#Service
#EnableCaching
#RequiredArgsConstructor
public class LabelServiceImpl implements LabelService {
private static final String CACHE_NAME = "demoCache";
private final LabelRepository labelRepository;
#Override
public List<LabelDTO> findByUuid(UUID uuid) {
final Label label = labelRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Not found."));
final List<LabelDTO> labelList = labelRepository.findByUuid(uuid);
return labelList;
}
}
Here is my Unit Test:
#Import({CacheConfig.class, LabelServiceImpl.class})
#ExtendWith(SpringExtension.class)
#EnableCaching
#ImportAutoConfiguration(classes = {
CacheAutoConfiguration.class,
RedisAutoConfiguration.class
})
#RunWith(MockitoJUnitRunner.class)
public class CachingTest {
#InjectMocks
private LabelServiceImpl labelService;
#Autowired
private CacheManager cacheManager;
#Mock
private LabelRepository labelRepository;
#Test
void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
}
}
When I use #Autowired for LabelServiceImpl in the Unit Test, I get "Nullpointer exception" error. If I use #InjectMocks annotation for that instance, then there is no error, but no caching is done (it calls labelService.findByUuid 2 times instead of 1).
I think I made a mistake related to the annotations in Unit Test class, but I tried many different combinations and cannot solve the problem.
So, how can I fix the problem?
Update: Finally I fixed the problem by using the following approach. However, I also had to update the mockito-core version from 3.3.3 to 3.7.0 as shown below. Otherwise I was getting "java.lang.NoSuchMethodError: org.mockito.MockitoAnnotations.openMocks(Ljava/lang/Object;)Ljava/lang/AutoCloseable;" error.
pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
CachingTest:
#ExtendWith(MockitoExtension.class)
#SpringBootTest
public class CachingTest {
#Autowired
#InjectMocks
private LabelServiceImpl labelService;
#MockBean
private LabelRepository labelRepository;
#Test
public void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
assertEquals(labelFromDb, labelFromCache);
}
}
Mockito does not simulate your springt boot application. It just mocks the annotated objects and injects your mock objects into your labelService.
On the other hand to test a spring boot application #Autowired is not enough.
You have to build a Spring Boot Integration Test.
To do this annotate your Test with #SpringBootTest.
This initializes a real Application Context.
try to autowire, LabelService
package com.demo;
import org.springframework.stereotype.Service;
#Service
public class LabelService
{
public String getLabel()
{
return "Test Label " + System.currentTimeMillis();
}
}
package com.demo;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
public class CachingTest
{
#Autowired
private LabelService labelService;
#Test
public void testLabelService()
{
assertNotNull(labelService);
}
}
this works

Springboot integration tests are failing when adding multiple service

I have a spring boot application with one controller class, one Service and one repository working perfectly fine. I have added Junit test cases for the same and that is also working perfectly fine.
#RestController
public class EmployeeController{
#Autowired
EmployeeService service;
#GetMapping("/list")
public List<Employee> getEmployee(){
return service.findAll();
}
}
#Service
public class EmployeeService{
#Autowired
EmployeeRepository repository;
public List<Employee> findAll(){
return repository.findAll();
}
}
#Repository
public interface EmployeeRepository extends JpaRepository<Employee, String>{}
The test class is below.
#RunWith(SpringRunner.class)
#WebMvcTest(value = EmployeeController.class)
class EmployeeControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#MockBean
private EmployeeService service;
#Test
public void findAllEmployeeTest(){
}
}
The test case is passing until here, but at the moment I am adding another API as below all tests are failing.
#RestController
public class DepartmentController{
#Autowired
DepartmentService service;
#GetMapping("/list")
public List<Department> getDepartment(){
return service.findAll();
}
}
#Service
public class DepartmentService{
#Autowired
DepartmentRepository repository;
public List<Department> findAll(){
return repository.findAll();
}
}
#Repository
public interface DepartmentRepository extends JpaRepository<Department, String>{}
The test class is below.
#RunWith(SpringRunner.class)
#WebMvcTest(value = DepartmentController.class)
class DepartmentControllerIntegrationTest {
#Autowired
private MockMvc mvc;
#MockBean
private DepartmentService service;
#Test
public void findAllDepartmentTest(){
}
}
After adding the Department services test cases are failing with below error:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentController': Unsatisfied dependency expressed through field 'departmentService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentService': Unsatisfied dependency expressed through field 'repository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.employeeapp.data.repository.DepartmentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Cheers!
For what i see, you try to do an IT test by mocking your service. In IT test you don't mock your service. In Integration Test you test that all work together. If you don't mock the repository it's more an E2E Test (end to end you test all the interaction path).
You can mock the repository if there is no logic to test, and/or don't want to write in your database.
So if it's unit test you can try that (it work on my projects) and i use Junit 5 (org.junit.jupiter)
#WebMvcTest(DepartementController.class)
public class DepartementControllerTest {
#MockBean
private DepartmentService departmentService;
#Autowired
MockMvc mockMvc;
#Test
public void findAllTest(){
// You can create a list for the mock to return
when(departmentService.findAll()).thenReturn(anyList<>);
//check if the controller return something
assertNotNull(departmentController.findAll());
//you check if the controller call the service
Mockito.verify(departmentService, Mockito.times(1)).findAll(anyList());
}
That's a unit test.
An It test will be more like
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class DepartmentIT {
#Autowired
DepartmentService departmentService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#BeforeEach
void setUp() {
this.mockMvc =
MockMvcBuilders.webAppContextSetup(this.wac)
.build();
}
#Test
void departmentFindAll() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/YOUR_CONTROLLER_URL")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is(200))
.andReturn();
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
assertTrue(mvcResult.getResponse().getContentAsString().contains("SOMETHING_EXPECTED"));
//You also can check its not an empty list, no errors are thrown etc...
}
the imports use so you dont search too much are
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
No qualifying bean of type 'com.employeeapp.data.repository.DepartmentRepository' available: expected at least 1 bean which qualifies as autowire candidate.
Spring looks for the bean of TYPE: DepartmentRepository.
Define the Bean
#Repository
public interface DepartmentRepository extends JpaRepository<Department, String>{}
Add the annotation here #Repository so Spring can turn DepartmentRepository into a Bean.
Autowire the Correct Type and Name
#Service
public class DepartmentService{
#Autowired
private DepartmentRepository repository; // The best practice is to write the full bean name here departmentRepository.
public List<Department> findAll(){
return repository.findAll();
}
}

Spring autowire of static configuration-object fails

as a Spring newbie I try to load external configuration into a Configuration-Object which I then try to autowire.
Following is my setup broken down to a bare minimum (less fields aso):
Package-structure:
app
|--Application.java
app.configuration
|--ConfigProperties.java
With my configuration.properties residing in:
src/main/resources/config
Application.java:
package app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import app.configuration.ConfigProperties;
#SpringBootApplication
public class Application
{
//just to test if autowire works
#Autowired
public static ConfigProperties config;
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
System.out.println(config.getId());
}
}
ConfigProperties.java:
package app.configuration;
import javax.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.validation.annotation.Validated;
#Configuration
#PropertySource("classpath:/config/configuration.properties")
#ConfigurationProperties
#Validated
public class ConfigProperties
{
#NotBlank
private String id;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
}
This results in a NullPointerException for the line:
System.out.println(config.getId());
in the Apllication.java.
I read several posts on this topic but most imply that own instantiation prevents autowiring, which I am not doing, or that the scan can't find the class to autowire, but from what I read with my annotations and package structure that should not be the problem.
Where is/are my mistake(s)?
PS: it seems that properties are read correctly from the property file. If I test the validation, the expected error is thrown for Null-value if leave out the part where I try to autowire.
#Autowired
public static ConfigProperties config;
You cannot autowire static fields in Spring.
//just to test if autowire works
You can write a new Junit test cases for that.
Something like:
#Autowired
public ConfigProperties config;
in your test class.
Since the annotation #Autowired requires getters/setters to inject the correct implementation, I don't suggest you to wire a static content. However, there is a workaround for this issue:
public static ConfigProperties config;
#Autowired
public Application(ConfigProperties config) {
Application.config= config;
}
Alternatively, take a look on the #PostConstruct annotation.
Do not use a static ConfigProperties . Then use #PostConstruct to print the ID from your config object.
#Autowired
ConfigProperties config;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
void printId(){
System.out.println("ID = " + config.getId());
}

#Autowired dependency in custom Spring Data Repository is null

I created a custom Spring Data repository like this
BaseRepositoryCustomImpl<T extends BaseEntity>
extends SimpleJpaRepository<T, Long>
implements BaseEntityRepository<T, Long> {
#Autowired
private MyCustomClass myCustomClass;
}
Note that MyCustomClass is defined in my config class as #Bean. Then use my custom repository by adding it to my config class like this
#Configuration
#Profile("jpa")
#EnableJpaRepositories(basePackages = {
"com.package.repository"
}, repositoryImplementationPostfix = "CustomImpl",
repositoryBaseClass = BaseRepositoryCustomImpl.class
)
Everything is working okay except for the myCustomClass, which is always null. How should I autowire MyCustomClass? It is auto-wiring properly if used in other classes like Controllers.
More generic problem is how to autowire instances for not spring managed types.
you can create BeanUtil class
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
and then use it the following way to get bean:
UserRepository userRepository = BeanUtil.getBean(UserRepository.class);
inspired by this

spring boot integration test (business layer)

I'm trying to setup integration tests for the business layer of a spring boot application. The Unit-Tests work fine but integration tests don't work. Here's the basic setup:
// entities
#Entity
Table(name="TOrder")
public class JPAOrder... {
}
#Entity
Table(name="TCustomer")
public class JPACustomer... {
}
// Repository interfaces
#Repository
public interface OrderRepository extends CrudRepository<JPAOrder, Long> {
...
}
#Repository
public interface CustomerRepository extends CrudRepository<JPACustomer, Long> {
...
}
// Business logic
#Service
#Transactional
public class OrderService ... {
...
#Autowired
private CustomerRepository customerRepository;
...
public Order createOrderForCustomer(final Order order, final Customer customer) {
...
}
}
// Test
#RunWith(SpringRunner.class)
#TestPropertySource(locations = "classpath:application-integrationtest.properties")
public class OrderIntegrationTest {
#SpyBean
private OrderRepository orderRepository;
#Autowired
private OrderService orderService;
Order order = orderService.createOrderForCustomer(...);
}
Starting the application gives me this error
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name '...repository.OrderRepository#0': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...repository.OrderRepository]: Specified class is an interface
...
If I don't use the #SpyBean annotation in the integration test the orderRepository in the OrderService is simply null.
I must be missing something really obvious but I can't figure out what.
Any suggestions?
For me also this exception occured. Please see This Question.
Try changing #TestPropertySource(..)
to
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
value={"spring.profiles.active=integrationtest"})
Hope it helps !

Categories