背景
后端新增了一个对算法badCase排查功能,通过用户传入的内容按照节点成功或者失败走不同分支流程处理,流程结构如图所示。 判定流程的底层功能通过service层以RPC接口形式提供服务,结构定义如下:
java
public ResponseDTO badCaseDetect(RequestDTO userInputParam);
输入参数为RequestDTO类型的userInputParam包括传入的数据集和指标信息以及用户query,输出FinalResult为各个步骤节点的判定结果。为了避免大量if-else,可以采用工厂策略或者状态模式优化结构,解除耦合,而且中途注入其他service出现了null的问题有对应记录,后续完善这部分优化内容。
service层功能完成以后,接下来就是构建controller层并进行调试。
一、controller层构建
RequestMapping我们自己来定义,可以支持get或者post方式请求,两者都是可以带请求体的。这里我们用post请求:
java
@Api(tags = "召回case排查接口")
@ResponseBody
@Controller
@RequestMapping("/recalling")
public class GroundingController{
private static final Logger LOGGER = LoggerFactory.getLogger(GroundingController.class);
@Resource
GroundingService groundingService;
@ApiOperation("recalling case 排查")
@ResponseBody
@RequestMapping(value = "/detect", method = RequestMethod.POST)
public GroundingCaseDetectResponseDTO groundingCaseDetect(@ApiParam("query") @RequestParam(value = "query") String query,
@ApiParam("Id") @RequestParam(value = "id") String datasetId,
@ApiParam("dimIds") @RequestParam(value = "dimIds") List<String> dimIds) throws Exception {
GroundingDTO groundingDTO = new GroundingDTO();
groundingDTO.setQuery(query);
groundingDTO.setId(Id);
groundingDTO.setDimIds(dimIds);
LOGGER.error("请求内容:{}",groundingDTO);
ResponseDTO finalResult = groundingService.badCaseDetect(groundingDTO);
LOGGER.error("最终结果:{}",finalResult);
return responseDTO;
}
}
二、controller层测试
测试方法包括很多种,但是不同的方式对问题排查的便捷性不同。
1.本地集成测试方式-----本地先启动工程,然后直接用postman发请求,获取响应信息,这种方式一般信息较少,如果出了问题可能会很难排查。
2.本地单元测试方式-----通过Spring Test集成的mockMVC来进行测试,支持通过mock或者直接引入真实的service进行测试,完美集成Spring框架,便于本地断点调试发现定位问题。
3.合并发布后测试---,先合并到boe环境分支,构建发布到线下测试环境,然后通过postman构建请求发送,如果产生问题,可以通过全局logId访问日志平台,分析调用链路,排查问题,方便更直观更快速找到原因,但是合并代码冲突修正需要和其他同事沟通好。
代码层面先写个单测模块,过程中引入了mockMVC,为了求快直接用GPT生成的demo了,最初代码不一定是正确的
java
public class GroundingControllerTest extends BaseTest{
private static final Logger LOGGER = LoggerFactory.getLogger(GroundingControllerTest.class);
@InjectMocks
private GroundingController controller;
@Mock
private GroundingService groundingService;
private MockMvc mockMvc;
public GroundingControllerTest() {
initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void testGroundingCaseDetect() throws Exception {
String query = "*****************";
String Id = "*********";
List<String> dimIds = Arrays.asList("**********","*************");
// 设置其他参数的值
String cookie = "**********************";
// 通过MockMvc发送请求,并传递查询参数
MockHttpServletResponse response = mockMvc.perform(MockMvcRequestBuilders.post("/grounding/detect")
.contentType(MediaType.APPLICATION_JSON)
//.header("Cookie", cookie)
.param("query", query)
.param("Id", Id)
.param("dimIds", String.join(",", dimIds))
// 验证响应状态为200(或其他预期的HTTP状态码)
.andExpect(status().isOk())
// 返回响应结果进行进一步验证和处理
.andReturn().getResponse();
// 根据你的需求进行验证和断言
LOGGER.error("测试结果:{}",response.getContentAsString());
}
}
1.先尝试本地postman测试
本地先启动工程,然后直接用postman发请求,获取响应信息,这种方式一般信息较少,出了问题很难排查。最初测试结果如下:
报错203状态码,想到自己没有传用户cookie,于是从线下环境发布的平台地址F12刷新随便获取了一个用户cookie补充到请求里面。
然后继续请求,发现报错501状态码---"第三方服务请求失败",这就有点看不懂了,于是转去进行mockMVC的单元测试。
2.使用mockMVC进行调试
之前的代码整体上结构看着问题不大,但是具体内容不一定正确,所以先运行一下这部分的test试下,发现最终结果是null,说明接口可能没有调用到。 于是我们可以以debug模式运行,发现问题出在 FinalResult finalResult = groundingService.badCaseDetect(groundingDTO); 运行到这一处时候,步入不了groundingService.badCaseDetect函数,直接跳过了,猜想可能是mock这个功能模拟了接口方法,并没有真实调用提供的接口方法(你猜它为什么叫mockMVC? 肯定是有mock功能的嘛),所以还是老老实实去官网查看对应的demo,把@Mock注解部分用真实的接口服务替换掉。
官网地址: chat.openai.com/c/150bc078-...
github上的demo: github.com/spring-proj... 并且可参考博客www.cnblogs.com/cxygg/p/174... (应该学会看官方的Api的demo,不是在别人的博客上面去抄)
对应修改之后的代码如下:
java
@AutoConfigureMockMvc
public class GroundingControllerTest extends BaseTest{
private static final Logger LOGGER = LoggerFactory.getLogger(GroundingControllerTest.class);
@Autowired
private GroundingController controller;
@Autowired
WebApplicationContext context;
//在before里面初始化
private MockMvc mockMvc;
@Before
public void before() {
//初始化mockMvc
//可以测试单个controller
//mockMvc = MockMvcBuilders.standaloneSetup(mockMvcController).build();
//可以测试所有controller
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
//..............后面都不用动
}
}
修改完成以后继续运行,发现程序跑通了,输出了预期的结果。
然后我们就可以愉快的合并代码发布到boe测试环境了,中途注意一点,合并代码时候,commit成功了,先本地测试一下,能正常跑通再push上去,不然提交了运行不了的代码,污染环境,就很烦。
总结
对于controller层的测试,后续推荐直接用Spring Test3.2版本之后提供的mockMVC功能,它支持单模块测试,也支持端到端测试;支持mock,也支持直接调用原始service接口,出了问题也方便我们本地debug断点调试定位问题,和Junit一样无缝集成Spirng框架,所以直接用mockMVC会方便很多。