Home > Blockchain >  In JUnit 5, why are postMethod throwing TransactionSystemException instead return ResponseEntity wit
In JUnit 5, why are postMethod throwing TransactionSystemException instead return ResponseEntity wit

Time:05-19

I'm pretty new to Spring Boot and JUnit, so im trying to build a Rest API with validation contraints like below:

Entity class:

@Entity
public class CareerLevel {

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

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

    // Getters and Setters
}

Controller:

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

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

When I test post method on Insomnia (or any other HTTP client) with invalid values (null, too long strings, etc) It's returning Bad Request (exactly how I expected). But when I run my Test class, It's throwing an exception called TransactionSystemException

Test Method:

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

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

How can I handle that exception and get response data?

CodePudding user response:

Hi I tried your code locally and added a lombok annotation to create a builder for entity.

see @Builder

I recommend using MockMvc when testing your Controller class.

I also used mockito to mock CareerLevelRepository and injected it into 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));
    }
}

It simply uses MockMvc to create a request rather than calling the method. ObjectMapper converts our created request to Json which has been sent as body.

CodePudding user response:

The reason your test works from HTTP client is because @Valid annotation validating all of your incoming requests & hence you are getting proper response i.e. if you use longer string than 50 for careerLevel then too long strings, if passing null then null value etc.

Same is not working in your junit because you are creating plain object of careerLevel & setting value. This will eventually gets passed to careerLevelRepository.save.

Your repository is trying to save this object into DB & throwing TransactionSystemException because of invalid input & mostly because commit got failed.

  • Related