java - 在 JUnit 5 中,为什么 postMethod 会抛出 TransactionSystemException 而不是返回带有错误请求的 ResponseEntity?

我对 Spring Boot 和 JUnit 还是很陌生,所以我尝试构建一个带有验证约束的 Rest API,如下所示:

实体类:

@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 客户端)上使用无效的 values(null、太长的字符串等)测试 post 方法时,它返回了错误的请求(完全符合我的预期)。但是当我运行我的测试类时,它抛出了一个名为 TransactionSystemException 的异常

测试方法:

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

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

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

回答1

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

https://projectlombok.org/features/Builder @Builder

我建议在测试控制器类时使用 MockMvc

我还使用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。

回答2

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

同样在您的 junit 中不起作用,因为您正在创建 careerLevel 的普通对象并设置 value。这最终将传递给 careerLevelRepository.save

您的 repository 正在尝试将此对象保存到数据库中并抛出 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/TransactionSystemException.html 因为输入无效并且主要是因为提交失败。

相似文章

最新文章