2

I am trying to write some tests for a repository in my spring boot application, however the repository is autowired as null. The code for the test class is as follows:

package jpa.project.repo;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ContextConfiguration;

import jpa.project.entity.Person;

@EnableAutoConfiguration
@ContextConfiguration(classes = PersonRepo.class)
@DataJpaTest
public class PersonRepoTest {

    @Autowired
    private PersonRepo personRepoTest;

    @Test
    public void testPersonRepo() throws Exception {

        Person toSave = new Person();
        toSave.setPersonId(23);

        if (personRepoTest == null) {
            System.out.println("NULL REPO FOUND");
        }

        personRepoTest.save(toSave);

        Person getFromDb = personRepoTest.findOne(23);

        Assert.assertTrue(getFromDb.getPersonId() == 23);
    }
}

The print statement does get printed when I run this file as JUnit test in Eclipse, which confirms the nullpointerexception that then comes. All of my tests are in the same packages as the main application, but the packages are under src/test/java. I tried a few changes with the packaging names, but that didn't help so I don't know what the problem is now. Why is the repo initialized to null?

Felipe Desiderati
  • 2,414
  • 3
  • 24
  • 42
ITWorker
  • 965
  • 2
  • 16
  • 39
  • I think you need to return once a personRepoTest is null? Since your `if` condition will execute but also the line `personRepoTest.save(toSave)` which would seem is a NullPointerException. – gtgaxiola Aug 01 '18 at 18:59
  • @gtgaxiola The print statement is just to confirm that it is null, but the question is why is it null (edited the question to better reflect this)? – ITWorker Aug 01 '18 at 19:02
  • why don't you use `@RunWith(SpringRunner.class) @DataJpaTest` ? – Dirk Deyne Aug 01 '18 at 19:07
  • @DirkDeyne I had that before but I was getting the `Failed to load ApplicationContext` error – ITWorker Aug 01 '18 at 19:09
  • @ITWorker can you startup application without errors? – Dirk Deyne Aug 01 '18 at 19:19
  • @DirkDeyne yes I can. – ITWorker Aug 01 '18 at 19:20
  • @ITWorker strange... If I use `@RunWith(SpringRunner.class) @EnableAutoConfiguration @ContextConfiguration(classes = PersonRepo.class)` my tests pass – Dirk Deyne Aug 01 '18 at 20:00
  • @DirkDeyne how do you run the test? Is it right click on the file in eclipse -> run as -> Junit test? – ITWorker Aug 01 '18 at 20:15
  • 1
    @ITWorker yes... note that your testclass above is missing `@RunWith(SpringRunner.class)` – Dirk Deyne Aug 01 '18 at 20:21
  • @DirkDeyne Ah that fixed it. Now I am getting a different error regarding SQL, which is better and something I think I can fix. Originally I was trying `@RunWith(SpringRunner.class) @DataJpaTest` without the other annotations, but now I left those ones intact and it seems to have helped. No more null repository error. Thanks! – ITWorker Aug 01 '18 at 20:26

2 Answers2

3

The problem when using Test Slicing like @DataJpaTest is the Spring Boot will only load a specific set of classes. See this part from Spring Documentation:

@DataJpaTest can be used if you want to test JPA applications. By default, it will configure an in-memory embedded database, scan for @Entity classes and configure Spring Data JPA repositories. Regular @Component beans will not be loaded into the ApplicationContext.

A @Repository is considered a regular @Component because it is not a Spring Data JPA repository (the interface is). So, it is not loaded. Only repository interfaces will be loaded by default, like this:

public interface PersonRepo extends JpaRepository<Person, Long> {
    ...
}

To fix your problem, you may force the @DataJpaTest to load all of you @Repository classes:

@DataJpaTest(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))

Or just import the @Repository class that you need in to your test:

@Import(PersonRepo.class)

Here is another similar problem: Spring test with @DataJpaTest can't autowire class with @Repository (but with interface repository works!)

Felipe Desiderati
  • 2,414
  • 3
  • 24
  • 42
  • hi, can you help me with this please : https://stackoverflow.com/questions/62313098/spring-boot-datajpatest-fail-with-java-lang-illegalstateexceptioncaused-by-giv – user1182625 Jun 11 '20 at 09:50
0

Here is a working example of unit test with @DataJpaTest and TestEntityManager:

PersonRepo extends JpaRepository and has @Repository annotation

I'am using this approach in my projects, if all your configuration is valid and application can run normally, test will pass.

@RunWith(SpringRunner.class)
@DataJpaTest
public class RepositoryTest {

    @Autowired
    TestEntityManager entityManager;

    @Autowired
    PersonRepo sut;

    @Test
    public void some_test() {
        Person toSave = new Person();
        toSave.setPersonId(23);

        entityManager.persistAndFlush(toSave);

        Person getFromDb = sut.findOne(23);
        Assert.assertTrue(getFromDb.getPersonId() == 23);
    } 
}
Emre Savcı
  • 3,034
  • 2
  • 16
  • 25
  • 1
    I tried this approach before posting the question, and it didn't work (error about needing a service that is autowired within the main application). My application has a scheduler, so I followed this SO thread as the basis for my attempt: https://stackoverflow.com/questions/50231209/test-spring-data-repository-in-a-project-without-a-spring-boot-application-main – ITWorker Aug 01 '18 at 19:18