You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot JPA RESTful API服务层与仓储层测试编写咨询

问题分析

你的测试失败核心原因是:使用@Spy注解TransactionRepository但未正确配置方法行为。由于TransactionRepository是Spring Data JPA接口,@Spy会生成一个无实际逻辑的代理对象,默认情况下save方法返回null、findAll返回空列表,导致断言不成立。

解决方案

一、服务层测试(用Mockito模拟仓储)

服务层测试应聚焦业务逻辑,无需依赖真实数据库。我们用@Mock模拟仓储,通过Mockito配置方法返回值并验证调用逻辑。

修正后的服务层测试代码

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 org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Assertions;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class TransactionServiceImplTest {

    @InjectMocks
    private TransactionServiceImpl transactionService;

    @Mock
    private TransactionRepository transactionRepository;

    @Test
    public void testAddTransaction() {
        TransactionModel transaction = new TransactionModel(1, 1, new Date(), 100000);
        when(transactionRepository.save(transaction)).thenReturn(transaction);

        TransactionModel savedTransaction = transactionService.addTransaction(transaction);
        Assertions.assertNotNull(savedTransaction);
        Assertions.assertEquals(1, savedTransaction.getId());
        verify(transactionRepository, times(1)).save(transaction);
    }

    @Test
    public void testGetAllTransactions() {
        TransactionModel transaction = new TransactionModel(1, 1, new Date(), 100000);
        when(transactionRepository.findAll()).thenReturn(List.of(transaction));

        List<TransactionModel> transactions = transactionService.findAllTransaction();
        Assertions.assertEquals(1, transactions.size());
        verify(transactionRepository, times(1)).findAll();
    }

    @Test
    public void testFindById() {
        TransactionModel transaction = new TransactionModel(1, 1, new Date(), 100000);
        when(transactionRepository.findById(1L)).thenReturn(Optional.of(transaction));

        Optional<TransactionModel> foundTransaction = transactionService.findById(1L);
        Assertions.assertTrue(foundTransaction.isPresent());
        Assertions.assertEquals(1, foundTransaction.get().getId());
        verify(transactionRepository, times(1)).findById(1L);
    }

    @Test
    public void testUpdateTransaction() {
        TransactionModel transaction = new TransactionModel(1, 1, new Date(), 100000);
        when(transactionRepository.save(transaction)).thenReturn(transaction);

        TransactionModel updatedTransaction = transactionService.updateTransaction(transaction);
        Assertions.assertNotNull(updatedTransaction);
        verify(transactionRepository, times(1)).save(transaction);
    }

    @Test
    public void testDeleteTransaction() {
        doNothing().when(transactionRepository).deleteById(1L);
        transactionService.deleteTransaction(1L);
        verify(transactionRepository, times(1)).deleteById(1L);
    }
}

关键要点

  • 用@Mock替代@Spy:@Mock创建完全模拟的仓储对象,通过when(...).thenReturn(...)配置方法返回值,避免默认返回null。
  • 验证方法调用:使用verify(...)确保服务层正确触发仓储的对应方法,保障业务逻辑流程正确。
  • 独立测试用例:每个测试单独准备数据和模拟行为,不依赖全局前置方法,避免测试间耦合。

二、仓储层测试(用H2内存数据库)

仓储层测试需要验证JPA映射、查询逻辑的正确性,我们用Spring Boot Test结合H2内存数据库模拟真实数据库环境。

1. 添加依赖

确保项目中包含以下测试依赖(以Maven为例):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

2. 编写仓储层测试代码

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

import id.ac.ui.cs.advpro.app.models.TransactionModel;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;

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

import static org.junit.jupiter.api.Assertions.*;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class TransactionRepositoryTest {

    @Autowired
    private TransactionRepository transactionRepository;

    @Test
    public void testSaveTransaction() {
        TransactionModel transaction = new TransactionModel(0, 1, new Date(), 100000);
        TransactionModel savedTransaction = transactionRepository.save(transaction);

        assertNotNull(savedTransaction);
        assertTrue(savedTransaction.getId() > 0);
        assertEquals(1, savedTransaction.getVoucherId());
    }

    @Test
    public void testFindAllTransactions() {
        TransactionModel transaction1 = new TransactionModel(0, 1, new Date(), 100000);
        TransactionModel transaction2 = new TransactionModel(0, 2, new Date(), 200000);
        transactionRepository.save(transaction1);
        transactionRepository.save(transaction2);

        List<TransactionModel> transactions = transactionRepository.findAll();
        assertEquals(2, transactions.size());
    }

    @Test
    public void testFindById() {
        TransactionModel transaction = new TransactionModel(0, 1, new Date(), 100000);
        TransactionModel savedTransaction = transactionRepository.save(transaction);

        Optional<TransactionModel> foundTransaction = transactionRepository.findById(savedTransaction.getId());
        assertTrue(foundTransaction.isPresent());
        assertEquals(savedTransaction.getId(), foundTransaction.get().getId());
    }

    @Test
    public void testDeleteTransaction() {
        TransactionModel transaction = new TransactionModel(0, 1, new Date(), 100000);
        TransactionModel savedTransaction = transactionRepository.save(transaction);

        transactionRepository.deleteById(savedTransaction.getId());
        Optional<TransactionModel> deletedTransaction = transactionRepository.findById(savedTransaction.getId());
        assertFalse(deletedTransaction.isPresent());
    }
}

关键要点

  • @DataJpaTest:自动配置JPA测试环境,包括内存数据库、EntityManager等核心组件。
  • @AutoConfigureTestDatabase:指定使用H2内存数据库替代真实数据库,避免测试污染生产数据。
  • 验证真实CRUD逻辑:测试JPA映射是否正确、自增ID是否生成、查询结果是否符合预期等底层逻辑。

内容的提问来源于stack exchange,提问作者Cheesewaffle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:49:52