I am using Spring Boot 1.4.4. Followed the Spring Boot Test article for unit tests. When I have custom repositories, test is not working and fails with error UnsatisfiedDependencyException: Error creating bean with name 'com.jay.UserRepositoryTest': Unsatisfied dependency expressed through field 'userRepository';
Here is my code,
#Repository
#Transactional
public class UserRepository {
#Autowired
private EntityManager entityManager;
// sample code for custom repo. can be done easily in CrudRepo
public User findUser(String name){
TypedQuery<User> q = entityManager.createQuery("Select u from User u Where u.name = :name", User.class);
q.setParameter("name", name);
return q.getSingleResult();
}
}
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository userRepository;
#Test
public void findUserTest(){
...
}
}
But I am able to test the following Dao with Spring Boot without any config change,
#Transactional
public interface UserDao extends CrudRepository<User, Long> {
User findByEmail(String email);
}
When I am using #SpringBootTest, I am able to inject UserRepository, but not TestEntityManager.
Posted the same question in Github of Spring Boot Test and got reply. Refer this
Related
I am trying to create junit test case using SpringJUnit4ClassRunner.
#Configuration
#ComponentScan(basePackages = { "com.controller",
"com.service",
"com.repository" })
class CustomConfiguration {
}
#RunWith(SpringJUnit4ClassRunner.class)
#org.springframework.test.context.ContextConfiguration(classes = CustomConfiguration.class)
public class Test {
#InjectMocks
#Spy
private EmployeeController employeeController;
#Mock
EmployeeService employeeService;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#org.junit.Test
public void test() throws Exception {
Employee employee = new Employee();
employee.setEmailId("admin#gmail.com");
employee.setFirstName("admin");
employee.setLastName("admin");
Employee employee = employeeController.createEmployee(employee);
assertNotNull(employee);
}
}
It is giving error of No qualifying bean of type EmployeeRepository.
It seems that even tough you are having a custom configuration class for your test, the repository beans are not getting created in background through classpath scanning. If you are aiming for an integration test case rather than a junit one since you doesn't seem to mock anything in the code that you provided , why don't you try using more updated annotation version like using SpringRunner.class instead of SpringJunit4Runner.class , if your spring version supports it. If you are just aiming to create a unit test case. Create a mock bean for whatever you want to mock like :
#Mock
SomeRepository repo;
This mock should be automatically injected to your service beans when it starts up by junit.
If you are on springboot then:
#RunWith(SpringRunner.class)
#SpringBootTest
public class Test {
#Mock
EmployeeService employeeService;
#InjectMocks
private EmployeeController employeeController;
#org.junit.Test
public void test() throws Exception {
Employee employee = new Employee();
employee.setEmailId("admin#gmail.com");
employee.setFirstName("admin");
employee.setLastName("admin");
when(employeeService.save(any)).thenReturn(employee);
Employee employee = employeeController.createEmployee(employee);
assertNotNull(employee);
}
}
The above is a typical example for unit test in springboot , but in your case for controller classes springboot provides the annotation #WebMvcTest or unit testing the web layer only.If you want to do it like that , read doc.
I do a simple web app but on start I get that error. I'm using Spring JPA. The message's about Spring could not find any bean to do autowired. But if I use CrudRepository I always have standard repository implementation. I can't get why Spring can't find own bean? What do I do wrong?
Service layer
#Controller
#RequestMapping("/api/v1/get")
public class UserService {
private final UserRepository repository;
#Autowired
public UserService(UserRepository repository) {
this.repository = repository;
}
Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Add annotation #Repository - it will allow Spring to discover this repository during Component Scan
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
I get it: I forgot to add default constructor for my model class. And if I use this class in Spring jpa CrudRepository<User, Long> it has to be with some default constructor.
I'm working on a project that uses SpringBoot 2.0.5 version, Spring Data JPA to persists and retrieve records using JPA. I autowired SimpleJpaRepository in the service layer. But while starting my application, it failed with
"NoSuchBeanDefinitionException"- No qualifying bean of type
'org.springframework.data.jpa.repository.support.SimpleJpaRepository<?, ?>'
available: expected at least 1 bean which qualifies as autowire candidate.
My controller, service and DAO are like below
Controller class:
#Controller
public class MyController{
#Autowired
private MyService<Person,PersonPK> service;
Service layer as
public interface MyService<V,K>{
methods defined
}
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private SimpleJpaRepository<V,K> repository; // This dependency is failing
}
Application as :
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
#EnableJpaRepositories
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Is my approach incorrect? Is that not the correct way of autowiring the SimpleJpaRepository.
There is no need for me to extend SimpleJpaRepository as Spring provided JPARepository is good for me, for now.
Thanks
You still need to create a repository interface that extends JpaRepisitory, or the spring repository type of your choice.
To quote the spring data documentation:
1.2.1 Defining repository interfaces
As a first step you define a domain class-specific repository interface. The interface must extend
Repository and be typed to the domain class and an ID type. If you
want to expose CRUD methods for that domain type, extend
CrudRepository instead of Repository.
Once you do create a new repository type, you will autowire by that type rather than the SimpleJpaRepository.
One way to get an implementation of the SimpleJpaRepository is by using a Configuration class to create an instance as a bean that you will use inside your service.
#Configuration
public class PersistanceConfiguration {
#PersistenceContext
private EntityManager entityManager;
#Bean
public SimpleJpaRepository<YourEntity, Long> getYourEntitySimpleRepository() {
return new SimpleJpaRepository<>(YourEntity.class, entityManager);
}
}
And inject it to your service as you would do with a JpaRepository, for example:
#Service
public class YourEntityServiceImpl<YourEntity, Long> implements YourEntityService {
private JpaRepository<YourEntity, K> repository;
private SimpleJpaRepository<YourEntity, K> simpleRepository;
#Autowired
public YourEntityServiceImpl(YourEntityRepository repository, SimpleJpaRepository<YourEntity, Long> simpleRepository) {
this.repository = repository;
this.simpleRepository = simpleRepository;
}
}
You should create a repository interface that extending JpaRepisitory.
#Repository
public interface MyRepository extends JpaRepisitory<T, ID> {
//
}
And the you should Autowired in your service class.
#Service("service")
public class MyServiceImpl<V,K> implements MyService<V,K>{
#Autowired
private MyRepository myRepository;
}
I would have a question concerning Spring Data - MongoDB and JUnit test.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { UserRepository.class, User.class })
public class MyJUnitTest {
The UserRepository looks like this:
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(final String username);
}
I get the following Exception:
Failed to instantiate [... .repository.UserRepository]: Specified class is an interface
My question now would be, how to do it, that UserRepository is instantiate although there is no implementation class because Spring Data does the implementation by its own? If I do not mark USerRepository with #Repository than Spring does not create a bean object
[EDIT]
I have tried the example of the link you posted and it works fine if I run the application over the main- method.
Then I tried to implement a test class but in this case I get the same exception:
Error creating bean with name 'hello.test.TestClass': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private hello.CustomerRepository hello.test.TestClass.repository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.CustomerRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
My test class looks like this in src/test/java/hello/test (hello.test is the package):
#ComponentScan("hello")
#EnableMongoRepositories(basePackages = "hello")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { CustomerRepository.class, Customer.class })
public class TestClass {
#Autowired
private CustomerRepository repository;
#Test
public void testMethod() {
System.out.println("repositoryd: " + repository);
}
}
and my CustomerRepository looks like this (with #Configuration annotation):
#Configuration
public interface CustomerRepository extends MongoRepository<Customer, String> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}
Actually I don't know which annotations I need in order to get the test running - Maybe you would have another suggestion in order that I can solve this issue.
For Spring Boot 1.5.8.RELEASE
You can use #SpringBootTest to bootstrap all you spring configurations.
Your test will look like
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeRepositoryTests {
#Autowired
private SomeRepository someRepository;
#Test
public void someTest() {
someRepository.someMethod(...);
// assertions
}
}
Of course you want to use embedded mongodb for test so add
for Maven
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
for Gradle
testCompile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
Your repo CustomerRepository doesn't require #Configuration or #Repository annotations. Spring will do it for you as you extends base Repository classes.
To setup Mongo Repositories you need to extend ApplicationContext with the following annotations.
#Configuration
#EnableAutoConfiguration // create MongoTemplate and MongoOperations
#EnableMongoRepositories(basePackages = "hello.test") // Create your repos
public class ApplicationConfig {
}
You also would like to use in-memory database for you unit/integration tests so them won't alter productions database.
To enable it just add the following dependency:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.2</version>
<scope>runtime</scope>
</dependency>
Finally, configure you test class with the ApplicationContext
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ApplicationConfig.class)
public class MyJUnitTest {
// Test cases ...
}
Within #SpringApplicationConfiguration you need to point to a configuration class. Neither UserRepository nor User probably are one I assume.
To get started with both Spring and Spring Data MongoDB fundamentals, be sure to check out this getting started guide.
If using junit 5 and you want to start up spring in the most lightweight way possible you will need the following annotations on your IntegrationTest class
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyMongoService.class})
#EnableMongoRepositories(basePackageClasses = MyRepository.class)
public class MyMongoIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private MyRepository myRepository;
#Autowired
private MyMongoService myMongoService; <-- Assuming this injects MyRepository
}
I am not being able to make #Autowire annotation work with a #Repository annotated class.
I have an interface:
public interface AccountRepository {
public Account findByUsername(String username);
public Account findById(long id);
public Account save(Account account);
}
And the class implementing the interface annotated with #Repository:
#Repository
public class AccountRepositoryImpl implements AccountRepository {
public Account findByUsername(String username){
//Implementing code
}
public Account findById(long id){
//Implementing code
}
public Account save(Account account){
//Implementing code
}
}
In another class, I need to use this repository to find an account by the username, so I am using autowiring, but I am checking if it works and the accountRepository instance is always null:
#Component
public class FooClass {
#Autowired
private AccountRepository accountRepository;
...
public barMethod(){
logger.debug(accountRepository == null ? "accountRepository is NULL" : "accountRepository IS NOT NULL");
}
}
I have also set the packages to scan for the components (sessionFactory.setPackagesToScan(new String [] {"com.foo.bar"});), and it does autowire other classes annotated with #Component for instance, but in this one annotated with #Repository, it is always null.
Am I missing something?
Your problem is most likely that you're instantiating the bean yourself with new, so that Spring isn't aware of it. Inject the bean instead, or make the bean #Configurable and use AspectJ.
It seems likely that you haven't configured your Spring annotations to be enabled. I would recommend taking a look at your component scanning annotations. For instance in a Java config application:
#ComponentScan(basePackages = { "com.foo" })
... or XML config:
<context:annotation-config />
<context:component-scan base-package="com.foo" />
If your FooClass is not under the base-packages defined in that configuration, then the #Autowired will be ignored.
As an additional point, I would recommend trying #Autowired(required = true) - that should cause your application to fail on start-up rather than waiting until you use the service to throw a NullPointerException. However, if annotations are not configured, then there will be no failure.
You should test that your autowiring is being done correctly, using a JUnit test.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
classes = MyConfig.class,
loader = AnnotationConfigContextLoader.class)
public class AccountRepositoryTest {
#Autowired
private AccountRepository accountRepository;
#Test
public void shouldWireRepository() {
assertNotNull(accountRepository);
}
}
This should indicate whether your basic configuration is correct. The next step, assuming that this is being deployed as a web application, would be to check that you have put the correct bits and pieces in your web.xml and foo-servlet.xml configurations to trigger Spring initialisation.
FooClass needs to be instancied by Spring to have his depencies managed.
Make sure FooClass is instancied as a bean (#Component or #Service annotation, or XML declaration).
Edit : sessionFactory.setPackagesToScan is looking for JPA/Hibernate annotations whereas #Repository is a Spring Context annotation.
AccountRepositoryImpl should be in the Spring component-scan scope
Regards,