如何使用JMockit对Spring MVC Controller进行单元测试?
嘿,我来给你一步步拆解怎么用JMockit完成Spring MVC Controller的单元测试,流程其实很清晰,咱们一步步来!
用JMockit实现Spring MVC Controller单元测试
1. 先搞定依赖配置
不管你用Maven还是Gradle,得把必要的依赖加上,确保能用到Spring测试工具、JMockit和JUnit:
Maven 依赖
<dependencies> <!-- Spring MVC 测试依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <!-- 排除默认的MockMvc依赖之外的冗余包(可选) --> <exclusions> <exclusion> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> </exclusion> </exclusions> </dependency> <!-- JMockit 依赖 --> <dependency> <groupId>org.jmockit</groupId> <artifactId>jmockit</artifactId> <version>1.49</version> <scope>test</scope> </dependency> <!-- JUnit 4 (如果用JUnit 5,换成junit-jupiter-api) --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>
Gradle 依赖
dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.jmockit:jmockit:1.49' testImplementation 'junit:junit:4.13.2' // 排除Mockito(如果不需要的话) testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'org.mockito' } }
2. 写个示例Controller
假设咱们有个处理用户信息的Controller,它依赖一个UserService来处理业务逻辑:
@Controller @RequestMapping("/users") public class UserController { private final UserService userService; // 推荐用构造注入,方便测试时注入Mock对象 public UserController(UserService userService) { this.userService = userService; } @GetMapping("/{id}") @ResponseBody public ResponseEntity<UserDto> getUserById(@PathVariable Long id) { UserDto user = userService.getUserById(id); if (user == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(user); } @PostMapping @ResponseBody public ResponseEntity<UserDto> createUser(@RequestBody UserDto userDto) { UserDto createdUser = userService.createUser(userDto); return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); } } // 配套的DTO和Service接口 class UserDto { private Long id; private String name; private String email; // 构造器、getter/setter 省略 } interface UserService { UserDto getUserById(Long id); UserDto createUser(UserDto userDto); }
3. 编写单元测试类
这部分是核心,咱们用JMockit来MockUserService,再配合Spring的MockMvc模拟HTTP请求,验证Controller的逻辑:
@RunWith(SpringJUnit4ClassRunner.class) @WebMvcTest(UserController.class) public class UserControllerTest { // 注入Spring提供的MockMvc,用来模拟HTTP请求 @Autowired private MockMvc mockMvc; // 用JMockit的@Injectable创建UserService的Mock实例,Spring会自动把它注入到Controller里 @Injectable private UserService mockUserService; @Test public void getUserById_ExistingId_ReturnsOk() throws Exception { // 1. 定义Mock的预期行为:调用getUserById(1L)时返回指定的用户数据 Long userId = 1L; UserDto mockUser = new UserDto(userId, "Alice", "alice@example.com"); new Expectations() {{ mockUserService.getUserById(userId); result = mockUser; }}; // 2. 模拟GET请求,验证响应状态和内容 mockMvc.perform(get("/users/{id}", userId) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(userId)) .andExpect(jsonPath("$.name").value("Alice")) .andExpect(jsonPath("$.email").value("alice@example.com")); // 3. 可选:验证Mock方法确实被调用了一次,确保逻辑走对了 new Verifications() {{ mockUserService.getUserById(userId); times = 1; }}; } @Test public void getUserById_NonExistingId_ReturnsNotFound() throws Exception { Long userId = 99L; // 定义Mock行为:调用不存在的ID时返回null new Expectations() {{ mockUserService.getUserById(userId); result = null; }}; // 验证响应是404 mockMvc.perform(get("/users/{id}", userId) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } @Test public void createUser_ValidDto_ReturnsCreated() throws Exception { UserDto inputDto = new UserDto(null, "Bob", "bob@example.com"); UserDto createdDto = new UserDto(2L, "Bob", "bob@example.com"); // 定义Mock行为:调用createUser时返回创建后的用户数据 new Expectations() {{ mockUserService.createUser(inputDto); result = createdDto; }}; // 构造请求体JSON ObjectMapper objectMapper = new ObjectMapper(); String requestBody = objectMapper.writeValueAsString(inputDto); // 模拟POST请求并验证响应 mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content(requestBody)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id").value(2L)) .andExpect(jsonPath("$.name").value("Bob")); } }
4. 核心要点解析
- @WebMvcTest:Spring专门为Controller测试设计的注解,它只会加载和MVC相关的Bean(比如Controller、HandlerMapping),不会加载Service、Repository等业务层Bean,测试速度更快,刚好配合JMockit的Mock对象使用。
- @Injectable:JMockit的注解,用来创建Mock对象,Spring会自动把这个Mock实例注入到Controller的依赖中(因为咱们用了构造注入,这也是推荐的注入方式)。
- Expectations块:用来定义Mock方法的预期行为,比如调用某个方法时返回什么值,或者抛出什么异常。
- Verifications块:用来验证Mock方法是否被正确调用,比如调用次数、参数是否符合预期,能帮你排查逻辑漏洞。
- MockMvc:Spring提供的工具,能模拟各种HTTP请求(GET/POST/PUT等),还能验证响应状态码、响应体内容、Header等,是Controller单元测试的核心工具。
内容的提问来源于stack exchange,提问作者Shadab Kazi
相关产品推荐
相关产品推荐

