You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:15:42