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 !
Related
I'd like to test my repository method. However when I run my test, it fails because of UnsatisfiedDependencyException. It for some reason tries to create AuthorizationServerConfig (or other bean if I remove #Configuration annotation from this one). It fails cause deeper in dependencies chain it requires RabbitMQ connection pool, that I prefer to not provide in repository test.
The question is why Spring tries to create all those beans not linked to repository logic?
I attempted to exclude all those beans with #DataMongoTest(exludeFilters: ...) and #DataMongoTest(exludeAutoConfiguration: ...) but it had no effect.
The only thing that helped was to add #Profile("!test") to all beans (all! controllers, services, components) in an application, but it smells like a very ugly solution.
Repository class is very simple:
#Repository
public interface ParticipantRepository extends MongoRepository<Participant, String> {
List<Participant> findAllByLoggedInTrueAndAccessTokenExpirationAfter(Date expirationAfter);
}
My test class:
#DataMongoTest()
#RunWith(SpringRunner.class)
public class ParticipantRepositoryTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private ParticipantRepository repository;
private List<Participant> participants;
#Before
public void setUp() throws Exception {
participants = createParticipants();
repository.saveAll(participants);
}
#After
public void tearDown() throws Exception {
repository.deleteAll();
}
#Test
public void findAllByLoggedInTrueAndExpirationAfter_shouldNotReturnLoggedOutParticipants() {
List<Participant> result = repository.findAllByLoggedInTrueAndAccessTokenExpirationAfter(new Date());
getLoggedOutParticipants().forEach(participant -> assertThat(participant, not(isIn(result))));
}
...
}
Running a test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
This error gets thrown only with unit tests, the program itself works.
My Interface
public interface MyService {
public String testMethod();
}
My Implementation
#Service
public class MyServiceImpl implements MyService{
#Autowired
private TransactionRepository transactionRepo;
#Autowired
private AccountRepository accountRepository;
#Autowired
private BankAccountStatementFactory baStatementFactory;
public String myMethod() {
return "Run My Method";
}
}
My Unit Test
#RunWith(SpringRunner.class)
#SpringBootTest
public class DeleteMeTest{
#Mock
private TransactionRepository transactionRepo;
#Mock
private AccountRepository accountRepository;
#Mock
private BankAccountStatementFactory baStatementFactory;
#InjectMocks
#Resource
MyServiceImpl myService;
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myService.myMethod();
System.out.println("My Unit Test");
}
}
Running this test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
A solution here is to inject the Interface, not the implementation into the unit test but this will not allow me to inject mocks.
Thats because an implementation is required with the #InjectMocks annotation. When I try to inject mocks into the interface I get the following exception:
Cannot instantiate #InjectMocks field named 'myService'! Cause: the type 'MyService' is an interface.
Just to be clear, all this worked in the beginning and went bad once I repackaged my classes. That could be the cause but not 100% sure.
Any hints on what might cause this BeanNotOfRequiredTypeException?
Thanks!
Since it's a unit test, you don't need Spring IMHO.
Simply initialize the tested class this way:
#InjectMocks
MyServiceImpl myService = new MyServiceImpl();
You can also remove these annotations:
#RunWith(SpringRunner.class)
#SpringBootTest
If you really need to use Spring (the reason for using Spring in the unit test is not clear from your post), you can try to unproxy the bean:
Have a separate declaration for the proxy and the bean:
#Resource
MyServiceImpl proxy;
#InjectMocks
MyServiceImpl myService;
Then initialize them in setUp():
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
myService = (MyServiceImpl)((TargetSource) proxy).getTarget();
MockitoAnnotations.initMocks(this);
}
I have written a sample spring boot application and it is failing to run with message
`
Description:
Field customerRepository in com.hibernatetutorial.service.CustomerServiceImpl required a bean of type 'com.hibernatetutorial.repository.CustomerRepository' that could not be found.
Action:
Consider defining a bean of type 'com.hibernatetutorial.repository.CustomerRepository' in your configuration.`
I have a #Repository annotation on CustomerRepository class and it's package is the there in base package scanning.
Below is configuration
#SpringBootApplication
#ComponentScan(basePackages="com.hibernatetutorial")
public class HibernateTutorialApplication {
public static void main(String[] args) {
SpringApplication.run(HibernateTutorialApplication.class, args);
}
}
#Repository
#Transactional
public interface CustomerRepository extends JpaRepository<Customer, UUID>{
}
#Service
#Transactional
public class CustomerServiceImpl implements CustomerService {
#Autowired
private CustomerRepository customerRepository;
public Customer createCustomer(Customer customer) {
return customerRepository.save(customer);
}
}
Customer entity is annotated with #Entity. Any suggestion if I miss anything
To make use of JpaRepository you need to add one of the following to your Application:
#EnableAutoConfiguration for Spring Boot to figure it out itself or
#EnableJpaRespositories(basePackageScan="com.example") to specify it yourself
For more information
Please verify your CustomerRepository and CustomerServiceImpl Java files are under the same packege com.hibernatetutorial.
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.
Following is the service.
#Service
public class MyService {
public List<Integer> getIds(Filter filter){
// Method body
}
}
And a configuration class.
#Configuration
public static class MyApplicationContext {
#Bean
public Filter filter(ApplicationContext context) {
return new Filter();
}
}
The desired goal is a unit test to confirm getIds() returns the correct result.
See the JUnit test below.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyApplicationContext.class,
loader=AnnotationConfigContextLoader.class)
public class AppTest
{
#Autowired
Filter filter;
#Autowired
MyService service;
}
The compiler finds the correct bean for the Filter class but throws a BeanCreationException: Could not autowire field exception for the service variable. I've tried adding the service class to the ContextConfiguration classes attribute but that results in a IllegalStateException: Failed to load ApplicationContext exception.
How can I add MyService to ContextConfiguration?
Add the following annotation to MyApplicationContext for the service to be scanned #ComponentScan("myservice.package.name")
Add these two annotations to the test class AppTest, like in the following example:
#RunWith(SpringRunner.class )
#SpringBootTest
public class ProtocolTransactionServiceTest {
#Autowired
private ProtocolTransactionService protocolTransactionService;
}
#SpringBootTest loads the whole context.