| package com.g9.g9backend.controller; |
| |
| import com.fasterxml.jackson.databind.ObjectMapper; |
| import com.g9.g9backend.pojo.*; |
| import com.g9.g9backend.pojo.DTO.*; |
| import com.g9.g9backend.service.*; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| import org.mockito.InjectMocks; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.springframework.http.MediaType; |
| import org.springframework.test.web.servlet.MockMvc; |
| import org.springframework.test.web.servlet.setup.MockMvcBuilders; |
| |
| import java.util.Date; |
| |
| import static org.mockito.ArgumentMatchers.*; |
| import static org.mockito.Mockito.when; |
| import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; |
| import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; |
| |
| /* |
| @SpringBootTest 启动一个完整的 Spring 应用上下文,用来测试整个应用的行为 |
| @AutoConfigureMockMvc 自动配置 MockMvc 对象,MockMvc 是 Spring 提供的工具,用来模拟 HTTP 请求,从而不需要启动真正的服务器来发送HTTP请求就能进行测试 |
| 注意@SpringBootTest 已经自动配置了 Mockito 或 Spring 的 Mock 环境,再在setup中手动调用 openMocks(this) 可能导致 Mock 对象被重复初始化 |
| 出现状态丢失问题(如 when().thenReturn() 规则失效),所以改为使用 JUnit 5 默认运行器,不需要在这里写注解 |
| */ |
| public class ResourceControllerTest { |
| |
| // MockMvc对象 |
| private MockMvc mockMvc; |
| |
| // @InjectMocks告诉测试框架(Mockito)创建 ResourceController 的实例,并自动将标注了 @Mock 的依赖注入到这个实例中 |
| @InjectMocks |
| private ResourceController resourceController; |
| |
| // @Mock告诉 Mockito 创建这些依赖的“模拟对象”,用来模拟服务行为,和controller层使用的service依赖一一对应即可 |
| @Mock |
| private ResourceService resourceService; |
| |
| @Mock |
| private GameplayService gameplayService; |
| |
| @Mock |
| private RewardService rewardService; |
| |
| @Mock |
| private UserUploadService userUploadService; |
| |
| @Mock |
| private CommunityService communityService; |
| |
| @Mock |
| private UserService userService; |
| |
| @Mock |
| private UserPurchaseService userPurchaseService; |
| |
| @Mock |
| private UserLikeService userLikeService; |
| |
| @Mock |
| private UserCollectionService userCollectionService; |
| |
| @Mock |
| private NotificationService notificationService; |
| |
| // ObjectMapper对象用于将 Java 对象和 JSON 字符串互相转换 ,因为在测试中,需要把请求参数或返回响应转换成 JSON 字符串形式来传输 |
| private final ObjectMapper objectMapper = new ObjectMapper(); |
| |
| // 这个方法会在每个测试方法执行之前运行。用来设置测试的初始化逻辑 |
| @BeforeEach |
| public void setup() { |
| MockitoAnnotations.openMocks(this); |
| mockMvc = MockMvcBuilders.standaloneSetup(resourceController).build(); |
| } |
| |
| // 以下是对各接口的测试,注意测试方法是要对每个接口的每个响应测试(如果一个接口有三种响应,就写三个测试方法) |
| // 上传资源 |
| @Test |
| public void testUploadResource() throws Exception { |
| |
| // 创建测试用的请求数据 |
| PostResourceDTO postResourceDTO = new PostResourceDTO(); |
| Resource resource = new Resource(); |
| resource.setResourceId(1); |
| resource.setResourceName("Test Resource"); |
| resource.setUploadTime(new Date()); |
| resource.setLastUpdateTime(new Date()); |
| resource.setPrice(1); |
| resource.setClassify("map"); |
| postResourceDTO.setResource(resource); |
| postResourceDTO.setGameplayList(new String[]{"Gameplay1", "Gameplay2"}); |
| postResourceDTO.setUserId(1); |
| |
| /* |
| 模拟服务层的行为,when(...).thenReturn(...)表示当某个服务方法被调用时,返回一个指定的结果(注意mybatis-plus的这些方法一般返回是boolean类型,所以写true不能写null) |
| 同样是测试方法,测试粒度有区别 |
| 有简单写法:when(gameplayService.save(any())).thenReturn(true);只验证方法是否被调用(间接验证逻辑是否执行到该服务方法),不关心方法调用的次数和传递的参数 |
| 也有复杂写法:额外用 verify 验证调用次数和 ArgumentCaptor 验证参数是否传递正确和InOrder 来验证方法的调用顺序是否正确... |
| */ |
| |
| // 测试主是要测试传参是否能正确接收,接口调用过程是否使用过这些方法(所以使用到的方法一般来说应该按顺序写在这,不能缺不能多也不能乱序,但是这里实际因为测试粒度不够细,没有对方法调用过程的验证,所以即是方法缺少或调用过程有问题,也不会报错),返回响应是否发送正确 |
| when(resourceService.save(any())).thenReturn(true); |
| when(gameplayService.save(any())).thenReturn(true); |
| when(userUploadService.save(any())).thenReturn(true); |
| when(communityService.save(any())).thenReturn(true); |
| when(rewardService.update(any())).thenReturn(true); |
| |
| // 模拟 HTTP 请求 |
| mockMvc.perform(post("/resource") // 使用 MockMvc 模拟用户提交 POST 请求到 /resource 接口 |
| .contentType(MediaType.APPLICATION_JSON) // 设置请求体是 JSON 格式 |
| .content(objectMapper.writeValueAsString(postResourceDTO))) //把请求参数对象:postResourceDTO 转成 JSON 字符串,作为请求体内容发送 |
| .andExpect(status().isOk()); // 期望接口返回 HTTP 状态码 200(成功) |
| } |
| |
| // 购买资源 |
| @Test |
| public void testPurchaseResource_success() throws Exception { |
| |
| // 用户积分足够 |
| User user = new User(); |
| user.setUserId(1); |
| user.setCredits(100); |
| Resource resource = new Resource(); |
| resource.setResourceId(1); |
| resource.setPrice(50); |
| |
| when(userService.getOne(any())).thenReturn(user); |
| when(resourceService.getOne(any())).thenReturn(resource); |
| when(userService.update(any())).thenReturn(true); |
| when(userPurchaseService.save(any())).thenReturn(true); |
| when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1)); |
| when(notificationService.save(any())).thenReturn(true); |
| |
| mockMvc.perform(post("/resource/purchase") |
| .contentType(MediaType.APPLICATION_JSON) |
| .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1)))) |
| .andExpect(status().isOk()); |
| } |
| |
| @Test |
| public void testPurchaseResource_purchaseFailed() throws Exception { |
| |
| //用户积分不足 |
| User user = new User(); |
| user.setUserId(1); |
| user.setCredits(10); |
| Resource resource = new Resource(); |
| resource.setResourceId(1); |
| resource.setPrice(50); |
| |
| when(userService.getOne(any())).thenReturn(user); |
| when(resourceService.getOne(any())).thenReturn(resource); |
| |
| mockMvc.perform(post("/resource/purchase") |
| .contentType(MediaType.APPLICATION_JSON) |
| .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1)))) |
| .andExpect(status().is(412)); |
| } |
| |
| // 点赞资源 |
| @Test |
| public void testLikeResource() throws Exception { |
| |
| User user = new User(); |
| user.setUserId(1); |
| Resource resource = new Resource(); |
| resource.setResourceId(1); |
| |
| when(userService.getOne(any())).thenReturn(user); |
| when(resourceService.getOne(any())).thenReturn(resource); |
| when(userLikeService.save(any())).thenReturn(true); |
| when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1)); |
| when(notificationService.save(any())).thenReturn(true); |
| |
| mockMvc.perform(post("/resource/like") |
| .contentType(MediaType.APPLICATION_JSON) |
| .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1)))) |
| .andExpect(status().isOk()); |
| } |
| |
| // 收藏资源 |
| @Test |
| public void testCollectResource() throws Exception { |
| |
| User user = new User(); |
| user.setUserId(1); |
| Resource resource = new Resource(); |
| resource.setResourceId(1); |
| |
| when(userService.getOne(any())).thenReturn(user); |
| when(resourceService.getOne(any())).thenReturn(resource); |
| when(userCollectionService.save(any())).thenReturn(true); |
| when(userUploadService.getOne(any())).thenReturn(new UserUpload(2, 1)); |
| when(notificationService.save(any())).thenReturn(true); |
| |
| mockMvc.perform(post("/resource/collection") |
| .contentType(MediaType.APPLICATION_JSON) |
| .content(objectMapper.writeValueAsString(new UserResourceDTO(1, 1)))) |
| .andExpect(status().isOk()); |
| } |
| } |