Написание тестов уровня сервиса и репозитория для JPA RESTful API в Springboot

вступление

Я пишу тесты для RESTful JPA Springboot API, но изо всех сил пытаюсь его настроить. Я установил простую модель, уровень репозитория и сервиса.

Модель

package id.ac.ui.cs.advpro.app.models;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import lombok.*;

import java.util.Date;

@Entity
@Table(name = "transactions")
public class TransactionModel {

    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    @Column(name = "id")
    @NotNull
    @Setter
    @Getter
    private long id;

    @Column(name = "voucher_id")
    @Setter
    @Getter
    private long voucherId;

    @Column(name = "transaction_date")
    @NotNull
    @Setter
    @Getter
    private Date transactionDate;

    @Column(name = "total_amount")
    @NotNull
    @Setter
    @Getter
    private long totalAmount;

    public TransactionModel() {
    }

    public TransactionModel(long id, long voucherId, Date transactionDate, long totalAmount) {
        this.id = id;
        this.voucherId = voucherId;
        this.transactionDate = transactionDate;
        this.totalAmount = totalAmount;
    }
}

Репозиторий

package id.ac.ui.cs.advpro.app.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import id.ac.ui.cs.advpro.app.models.TransactionModel;

@Repository
public interface TransactionRepository extends JpaRepository<TransactionModel, Long>{
}

Услуга

package id.ac.ui.cs.advpro.app.service.impl;

import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Service;
import id.ac.ui.cs.advpro.app.models.TransactionModel;
import id.ac.ui.cs.advpro.app.repository.TransactionRepository;
import id.ac.ui.cs.advpro.app.service.TransactionService;

@Service
public class TransactionServiceImpl implements TransactionService{

    private final TransactionRepository repository;

    public TransactionServiceImpl(TransactionRepository repository) {
        this.repository = repository;
    }

    //C(reate)
    @Override
    public TransactionModel addTransaction(TransactionModel transaction) {
        return repository.save(transaction);
    };

    //R(ead)
    @Override
    public List<TransactionModel> findAllTransaction() {
        return repository.findAll();
    };

    @Override
    public Optional<TransactionModel> findById(Long id) {
        return repository.findById(id);
    };

    //U(pdate)
    @Override
    public TransactionModel updateTransaction(TransactionModel transaction) {
        return repository.save(transaction);
    };

    //D(elete)
    @Override
    public void deleteTransaction(Long id) {
        repository.deleteById(id);
    };
}

Попытки

Вот мои попытки провести тесты для уровня обслуживания. Я просмотрел ранее ответившие и предложенные здесь решения, но, похоже, ни одно из них не работает.

package id.ac.ui.cs.advpro.app.service.impl;
import id.ac.ui.cs.advpro.app.models.TransactionModel;
import id.ac.ui.cs.advpro.app.repository.TransactionRepository;

import java.util.Date;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Assertions;


import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.InjectMocks;
import org.mockito.Spy;


@ExtendWith(MockitoExtension.class)
public class TransactionServiceImplTest {

    @InjectMocks
    private TransactionServiceImpl transactionService;

    @Spy
    private TransactionRepository transactionRepository;

    @BeforeEach
    public void setUp() {
        TransactionModel transaction = new TransactionModel(1, 1, new Date(), 100000);
        transactionService.addTransaction(transaction);
    }

    @Test
    public void testGetAllTransactions() {
        Assertions.assertEquals(1, transactionService.findAllTransaction().size());
    }
}

тестовый класс успешно внедрил репозиторий в сервисный класс, но

transactionService.addTransaction(transaction);

возвращает ноль, он вызывает

repository.save(transaction);

который, как говорят документы, никогда не должен возвращать ноль.

<TransactionModel> TransactionModel org.springframework.data.repository.CrudRepository.save(TransactionModel entity)
Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.

Type Parameters:

Parameters:

entity must not be null.
Returns:

the saved entity; will never be null.
Throws:

IllegalArgumentException - in case the given entity is null.
OptimisticLockingFailureException - when the entity uses optimistic locking and has a version attribute with a different value from that found in the persistence store. Also thrown if the entity is assumed to be present but does not exist in the database.

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

Вопрос

Как мне написать тесты для уровней репозитория и сервисов в моем проекте?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы написали так называемый «юнитовый» тест. Модульные тесты не предназначены для работы с базой данных — для этого мы пишем так называемые «интеграционные» тесты. Репозиторий — это интерфейс, и аннотация Spy создает для него макет. Mock-объект возвращает значение null для каждого метода. Что вам нужно, так это интеграционный тест. В таком случае вы поднимаете реальное приложение со всеми зависимостями и реальным взаимодействием с базой данных. Для этого замените @ExtendWith на @SpringBootTest и @Spy на @Autowired и запустите тест. Это должно пройти.

Если вы хотите протестировать определенный уровень вашего приложения, на самом деле это срезовое тестирование. @DataJpaTest — ответ на ваш вопрос.

@DataJpaTest
public class AccountRepositoryTest {
}

Он будет настраивать только bean-компоненты, помеченные @Repository. По умолчанию он использует базу данных в памяти, которую вы можете переопределить с помощью @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) (не рекомендуется для тестирования)

Об этом можно прочитать здесь

Другие вопросы по теме