如何为含protected权限校验方法的Controller编写单元测试
解决Spring Controller单元测试中父类protected权限校验方法的空指针问题
原Controller代码
@RestController @RequestMapping(value = ENDPOINT_URL) public class MainController extends RestControllerBase { MainService mainService; public MainController(MainService mainService) { this.mainService = mainService; } @GetMapping public ResponseEntity<List<MainDataDto>> getDashboardData() { checkPermission(); List<MainDataDto> result = mainService.getData(); return ResponseEntity.ok().body(result); } }
原测试代码
class MainControllerTest { MainController mainController; @Test public void test_getDashboardData_shouldReturn200Response() { MainService mainService = Mockito.mock(MainService.class); mainController = new MainController(mainService); List<MainDataDto> mockData = List.of(new MainDataDto()); when(mainService.getData()).thenReturn(mockData); Assertions.assertEquals(HttpStatus.OK, mainController.getDashboardData().getStatusCode()); } }
抛出的异常
java.lang.NullPointerException: Cannot invoke "org.springframework.security.core.Authentication.getPrincipal()" because the return value of "org.springframework.security.core.context.SecurityContext.getAuthentication()" is null
方法1:模拟Spring Security上下文(推荐)
直接在测试中初始化SecurityContext,模拟合法的Authentication对象,让checkPermission方法能正常执行,更贴近真实运行场景:
class MainControllerTest { MainController mainController; @BeforeEach void setUp() { // 模拟Authentication和Principal对象 Authentication auth = Mockito.mock(Authentication.class); // 若checkPermission需使用Principal的具体属性,可进一步mock对应逻辑 when(auth.getPrincipal()).thenReturn("test-user"); SecurityContext securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getAuthentication()).thenReturn(auth); SecurityContextHolder.setContext(securityContext); // 初始化Controller MainService mainService = Mockito.mock(MainService.class); mainController = new MainController(mainService); } @AfterEach void tearDown() { // 测试结束后清空上下文,避免影响其他测试用例 SecurityContextHolder.clearContext(); } @Test public void test_getDashboardData_shouldReturn200Response() { List<MainDataDto> mockData = List.of(new MainDataDto()); when(mainController.mainService.getData()).thenReturn(mockData); ResponseEntity<List<MainDataDto>> response = mainController.getDashboardData(); Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); Assertions.assertEquals(mockData, response.getBody()); } }
方法2:使用Mockito Spy跳过protected方法执行
若无需验证checkPermission的逻辑,仅需测试Controller核心业务流程,可通过Mockito Spy部分Mock Controller,跳过权限校验方法的实际执行:
class MainControllerTest { @Test public void test_getDashboardData_shouldReturn200Response() { MainService mainService = Mockito.mock(MainService.class); // 创建Controller的Spy对象,保留真实对象的部分逻辑 MainController mainController = Mockito.spy(new MainController(mainService)); // 跳过checkPermission方法的执行 Mockito.doNothing().when(mainController).checkPermission(); List<MainDataDto> mockData = List.of(new MainDataDto()); when(mainService.getData()).thenReturn(mockData); ResponseEntity<List<MainDataDto>> response = mainController.getDashboardData(); Assertions.assertEquals(HttpStatus.OK, response.getStatusCode()); } }
注意事项
- 方法1适合需要验证权限校验逻辑的场景,确保
checkPermission在测试环境下能正常工作; - 方法2适合快速跳过权限校验,聚焦Controller业务逻辑的测试场景;
- 若
checkPermission为final方法,Mockito Spy无法生效,此时只能使用方法1。
内容的提问来源于stack exchange,提问作者aCarella
相关产品推荐
相关产品推荐

