Экземпляр @Repository имеет значение null при вызове метода с использованием экземпляра @Service из модульного теста - PullRequest
1 голос
/ 10 июля 2020

Моя цель - использовать базу данных в памяти для этих модульных тестов, и эти зависимости перечислены как:

implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")

Так, чтобы экземпляр репозитория действительно взаимодействовал с БД, а я не просто имитирую возвращаемые значения.

Проблема в том, что когда я запускаю свой модульный тест, экземпляр репозитория внутри экземпляра службы имеет вид null.

Почему это так? Мне не хватает какой-то аннотации в классе модульного теста для инициализации экземпляра репозитория?

Это вывод консоли при запуске моего модульного теста:

null

java.lang.NullPointerException
    at com.my.MyService.findAll(MyService.java:20)
    at com.my.MyTest.testMy(MyTest.java:23)

Мой класс модульного теста:

public class MyTest {

  @MockBean
  MyRepository myRepository;

  @Test
  void testMy() {
    MyService myService = new MyService();
    int size = myService.findAll().size();
    Assertions.assertEquals(0, size);
  }
}

Мой класс обслуживания:

@Service
public class MyService {

    @Autowired
    MyRepository myRepository;

    public List<MyEntity> findAll() {

        System.out.println(myRepository); // null
        return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
    }

    @Transactional
    public MyEntity create(MyEntity myEntity) {

        myRepository.save(myEntity);

        return myEntity;
    }
}

Мой класс репозитория:

@Repository
public interface MyRepository extends CrudRepository<MyEntity, Long> {

}

Мой класс сущности:

@Entity
public class MyEntity {

    @Id
    @GeneratedValue
    public Long id;
}

Ответы [ 4 ]

3 голосов
/ 10 июля 2020

Почему? Мне не хватает какой-то аннотации в классе модульного теста для инициализации экземпляра репозитория?

В основном да

Другая проблема заключается в том, что вы создаете свой MyService Object вручную. Таким образом SpringBoot не имеет возможности внедрить за вас Bean. Вы можете исправить это, просто вставив свой MyService в свой Testclass. Ваш код должен выглядеть примерно так:

@SpringBootTest
public class MyTest {

    @Autowired
    private MyService myService;

    @Test
    void testMy() {
        int size = myService.findAll().size();
        assertEquals(0, size);
    }
}
1 голос
/ 10 июля 2020

Чтобы использовать аннотацию @MockBean, вы должны использовать SpringRunner для запуска теста. Используйте аннотацию @RunWith поверх тестового класса и передайте SpringRunner.class.

@RunWith(SpringRunner.class)
public class MyTest {

  @MockBean
  MyRepository myRepository;

  @Test
  void testMy() {
    MyService myService = new MyService();
    int size = myService.findAll().size();
    Assertions.assertEquals(0, size);
  }
}
0 голосов
/ 10 июля 2020

Проблема здесь в реализации вашего сервиса. Использование @Autowired для внедрения зависимости будет работать, когда вы запустите все приложение, но это не позволит вам внедрить пользовательскую зависимость, когда она вам понадобится, и хорошим примером этого является тестирование.

Измените реализацию службы на:

@Service
public class MyService {

    private MyRepository myRepository;

    public MyService(MyRepository myRepository){
        this.myRepository = myRepository;
    }

    public List<MyEntity> findAll() {

        System.out.println(myRepository); // null
        return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
    }

    @Transactional
    public MyEntity create(MyEntity myEntity) {

        myRepository.save(myEntity);

        return myEntity;
    }
}

Этот конструктор будет вызываться весной. Затем измените свой тест на:

public class MyTest {

  @Mock
  MyRepository myRepository;

  @Test
  void testMy() {
    MyService myService = new MyService(myRepository);
    int size = myService.findAll().size();
    Assertions.assertEquals(0, size);
  }
}

Примечание. Я заменил @MockBean на @Mock, поскольку предыдущая аннотация предназначена для внедрения фиктивного bean-компонента в контекст Spring, который не нужен, если вы делаете модульное тестирование. Если вы хотите загрузить контекст Spring (что я бы вам не рекомендовал), вам необходимо настроить свой тестовый класс с помощью @SpringBootTest или некоторых других доступных альтернатив. Это превратит ваш тест в интеграционный.

PD: Этот тест не будет работать, если вы не предоставите имитацию для myRepository.findAll(). Поведение Mockito по умолчанию - вернуть null, но вы ожидаете, что он вернет 0, поэтому вам нужно сделать что-то вроде given(myRepository.findAll()).willReturn(0).

0 голосов
/ 10 июля 2020

Я полагаю, вы должны sh написать интеграционный тест. Здесь вы можете удалить аннотацию MockBean и просто автоматически подключить свой репозиторий. Также запустите с классом SpringRunner.

@RunWith(SpringRunner.class)
public class MyTest {

  @Autowired
  MyRepository myRepository;

  @Autowired
  MyService myService
  
  @Test
  void testMy() {
   int size = myService.findAll().size();
   Assertions.assertEquals(0, size);
  }
}

Это должно работать

...