blob: 61b73d87b2e5f17ef695431b8cfe42e75e038320 [file] [log] [blame]
package com.g9.g9backend.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.ArrayList;
import java.util.Date;
import java.util.List;
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;
@Mock
private ResourceVersionService resourceVersionService;
@Mock
private SearchHistoryService searchHistoryService;
@Mock
private GameVersionService gameVersionService;
@Mock
private TorrentRecordService torrentRecordService;
@Mock
private HotTrendService hotTrendService;
// 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 testUploadResourceVersion() throws Exception {
ResourceVersion resourceVersion = new ResourceVersion();
resourceVersion.setResourceId(1);
resourceVersion.setResourceVersionName("Test ResourceVersion");
when(resourceVersionService.save(any())).thenReturn(true);
// 模拟 HTTP 请求
mockMvc.perform(post("/resource/version")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(resourceVersion)))
.andExpect(status().isOk());
}
// 购买资源
@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());
}
// 删除资源
@Test
public void testDeleteResource_success() throws Exception {
// 密码正确
when(userService.getOne(any())).thenReturn(new User(1, "hcy", "123", null, 0, 0, null, 0, 0, 0));
when(resourceService.removeById(1)).thenReturn(true);
mockMvc.perform(delete("/resource")
.param("resourceId", "1")
.param("userId", "1")
.param("password", "123"))
.andExpect(status().isNoContent());
}
@Test
public void testDeleteResource_wrongPassword() throws Exception {
// 密码错误
when(userService.getOne(any())).thenReturn(new User(1, "hcy", "123", null, 0, 0, null, 0, 0, 0));
mockMvc.perform(delete("/resource")
.param("resourceId", "1")
.param("userId", "1")
.param("password", "wrong"))
.andExpect(status().is(408));
}
// 取消点赞
@Test
public void testLikeDelete() throws Exception {
when(userLikeService.remove(any())).thenReturn(true);
mockMvc.perform(delete("/resource/like")
.param("userId", "1")
.param("resourceId", "1"))
.andExpect(status().isNoContent());
}
// 取消收藏
@Test
public void testCollectionDelete() throws Exception {
when(userCollectionService.remove(any())).thenReturn(true);
mockMvc.perform(delete("/resource/collection")
.param("userId", "1")
.param("resourceId", "1"))
.andExpect(status().isNoContent());
}
// 搜索资源
@Test
public void testGetResourceSearch() throws Exception {
when(searchHistoryService.save(any())).thenReturn(true);
when(resourceService.page(any(), any())).thenReturn(new Page<>(1, 1));
List<Gameplay> gameplayList = new ArrayList<>();
QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
gameplayQuery.eq("resource_id", 1);
when(gameplayService.list(gameplayQuery)).thenReturn(gameplayList);
mockMvc.perform(get("/resource/search")
.param("userId", "1")
.param("searchValue", "")
.param("classify", "")
.param("gameplayList", "")
.param("gameVersionList", "")
.param("pageNumber", "1")
.param("rows", "1"))
.andExpect(status().isOk());
}
// 获取资源信息
@Test
public void testGetResourceInfo() throws Exception {
when(resourceService.getById(any())).thenReturn(new Resource());
when(gameplayService.list(new QueryWrapper<Gameplay>().eq("resource_id", 1))).thenReturn(new ArrayList<>());
when(resourceVersionService.list(new QueryWrapper<ResourceVersion>().eq("resource_id", 1))).thenReturn(new ArrayList<>());
when(gameVersionService.list(new QueryWrapper<GameVersion>().eq("resource_version_id", 1))).thenReturn(new ArrayList<>());
when(torrentRecordService.list(new QueryWrapper<TorrentRecord>().eq("resource_version_id", 1))).thenReturn(new ArrayList<>());
when(userCollectionService.getOne(any())).thenReturn(new UserCollection());
when(userLikeService.getOne(any())).thenReturn(new UserLike());
when(userPurchaseService.getOne(any())).thenReturn(new UserPurchase());
when(userUploadService.getOne(any())).thenReturn(new UserUpload());
mockMvc.perform(get("/resource/info")
.param("resourceId", "1")
.param("userId", "1"))
.andExpect(status().isOk());
}
// 获取热门资源
@Test
public void testGetHotResource() throws Exception {
when(resourceService.page(any(), any())).thenReturn(new Page<>(1, 1));
List<Gameplay> gameplayList = new ArrayList<>();
QueryWrapper<Gameplay> gameplayQuery = new QueryWrapper<>();
gameplayQuery.eq("resource_id", 1);
when(gameplayService.list(gameplayQuery)).thenReturn(gameplayList);
mockMvc.perform(get("/resource/hot")
.param("classify", "map")
.param("pageNumber", "1")
.param("rows", "1"))
.andExpect(status().isOk());
}
// 获取热门资源幻灯片数据
@Test
public void testGetHotSlideResource() throws Exception {
List<Resource> resourceList = new ArrayList<>();
QueryWrapper<Resource> resourceQuery = new QueryWrapper<>();
resourceQuery.orderByDesc("(downloads * 0.25 + likes * 0.25 + collections * 0.25 + comments * 0.25)");
resourceQuery.last("LIMIT 3");
when(resourceService.list(resourceQuery)).thenReturn(resourceList);
mockMvc.perform(get("/resource/hot/slide"))
.andExpect(status().isOk());
}
// 获取热门资源趋势图
@Test
public void testGetResourceHotTrend() throws Exception {
List<HotTrend> hotTrendList = new ArrayList<>();
when(hotTrendService.list()).thenReturn(hotTrendList);
mockMvc.perform(get("/resource/hot-trend"))
.andExpect(status().isOk());
}
// 修改资源信息
@Test
public void testPutResourceInfo_success() throws Exception {
PutResourceInfoDTO putResourceInfoDTO = new PutResourceInfoDTO();
putResourceInfoDTO.setResourceId(1);
putResourceInfoDTO.setResourceName("test");
putResourceInfoDTO.setResourcePicture("test");
putResourceInfoDTO.setResourceSummary("test");
putResourceInfoDTO.setResourceDetail("test");
putResourceInfoDTO.setPrice(100);
String[] gameplayList = {"test1", "test2"};
putResourceInfoDTO.setGameplayList(gameplayList);
when(resourceService.getOne(any())).thenReturn(null);
when(resourceService.update(any())).thenReturn(true);
when(gameplayService.remove(any())).thenReturn(true);
when(gameplayService.save(any())).thenReturn(true);
mockMvc.perform(put("/resource/info")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(putResourceInfoDTO)))
.andExpect(status().isOk());
}
@Test
public void testPutResourceInfo__resourceNameExists() throws Exception {
PutResourceInfoDTO putResourceInfoDTO = new PutResourceInfoDTO();
putResourceInfoDTO.setResourceId(1);
putResourceInfoDTO.setResourceName("test");
putResourceInfoDTO.setResourcePicture("test");
putResourceInfoDTO.setResourceSummary("test");
putResourceInfoDTO.setResourceDetail("test");
putResourceInfoDTO.setPrice(100);
String[] gameplayList = {"test1", "test2"};
putResourceInfoDTO.setGameplayList(gameplayList);
when(resourceService.getOne(any())).thenReturn(new Resource());
mockMvc.perform(put("/resource/info")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(putResourceInfoDTO)))
.andExpect(status().is(411));
}
}