在 JUnit 5 中,为什么 postMethod 抛出 TransactionSystemException 而不是 return ResponseEntity with bad request?

In JUnit 5, why are postMethod throwing TransactionSystemException instead return ResponseEntity with bad request?

我是 Spring Boot 和 JUnit 的新手,所以我正在尝试构建一个具有如下验证约束的 Rest API:

实体class:

@Entity
public class CareerLevel {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer careerLevelId;

    @NotNull
    @NotEmpty
    @Size(max = 50)
    private String careerLevel;

    // Getters and Setters
}

控制器:

@PostMapping
public ResponseEntity<CareerLevel> postCareerLevel(@RequestBody @Valid CareerLevel careerLevel) {

    // saving data
    CareerLevel body = careerLevelRepository.save(careerLevel);
    return ResponseEntity.status(201).body(body);
}

当我使用无效值(空值、太长的字符串等)在 Insomnia(或任何其他 HTTP 客户端)上测试 post 方法时,它返回错误请求(正是我所期望的)。但是当我 运行 我的测试 class 时,它抛出了一个名为 TransactionSystemException

的异常

测试方法:

@Test
void testPostCareerLevelInvalidValues() {
    CareerLevel careerLevel = new CareerLevel();
    careerLevel.setCareerLevel("tooLongStriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing");

    ResponseEntity<CareerLevel> response1 = controller.postCareerLevel(careerLevel); // throwing TransactionSystemException 
    assertEquals(400, response1.getStatusCode().value());
}

如何处理该异常并获取响应数据?

您好,我在本地尝试了您的代码并添加了一个 lombok 注释来为实体创建一个构建器。

see @Builder

我建议在测试您的控制器时使用 MockMvc class。

我也用mockito模拟了CareerLevelRepository注入到CareerLevelController

@ExtendWith(MockitoExtension.class)
class CareerLevelControllerTest {
    @Mock
    private CareerLevelRepository careerLevelRepository;
    @InjectMocks
    private CareerLevelController careerLevelController;
    private MockMvc mockMvc;

    @BeforeEach
    void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(careerLevelController)
                .build();
    }
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Test
    void testPostCareerLevelInvalidValues() throws Exception {
        CareerLevel request = CareerLevel.builder()
                .careerLevel("tooLongStriiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing")
                .build();

        this.mockMvc
                .perform(post("/career-levels").contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().is(400));
    }
}

它只是使用 MockMvc 创建请求而不是调用方法。 ObjectMapper 将我们创建的 request 转换为 Json 已作为正文发送。

您的测试从 HTTP 客户端运行的原因是因为 @Valid 批注验证了您所有的传入请求,因此您得到了正确的响应,即如果您对 careerLevel 使用的字符串长于 50,那么也是如此长字符串,如果传递 null 那么空值等

Same 在您的 junit 中不起作用,因为您正在创建 careerLevel 和设置值的普通对象。这最终会传递给 careerLevelRepository.save.

您的 repository 正在尝试将此对象保存到数据库中并抛出 TransactionSystemException 因为输入无效并且主要是因为提交失败。