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
相关产品推荐
相关产品推荐

