Solucionado (ver solução)
Solucionado
(ver solução)
8
respostas

[Dúvida] Problema com Teste Unitário.

Olá Professor, Tudo bom? Estou com umas dúvidas em relação a um teste unitário para minha classe controller, que tenho que fazer para minha aplicação. Onde o método que criei deveria reclamar sobre uma lista que tem objetos com atributos nulos, ou seja, não se pode enviar parâmetros nulos. Contudo, ao executar os testes conforme o código da classe ReportControllerTeste, o teste roda certinho, mesmo com o isCreated. Contudo, se eu subo a aplicação e no POSTMAN eu tento enviar com parâmetros nulos, eu tomo um error 500, pois ele executa os @notNull que tenho na classe Domain e DTO.

Código da classe teste:

    @Test
    @Transactional
    void shouldComplainAboutListWithObjectsNulls() throws Exception{
        int databaseSizeBeforeCreate = reportRepository.findAll().size();


        List<SReportDTO> dtoList = new ArrayList<>();
        ReportDTO teste1 = new ReportDTO();
        dtoList.add(teste1);    << aqui eu abri o array e adicionei o teste1, para que os atributos já ficassem nulos. O teste não deveria criar objetos nulos.

        restReportMockMvc
            .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(dtoList)))
            .andExpect(status().isCreated());

Código da classe Controller:

   @PostMapping("/report") //Method to post one or more reports.
    public ResponseEntity<Object> saveListReport(@RequestBody @Valid List<ReportDTO> reportDTOList) {
        log.debug("REST request to save reports: {}", reportDTOList);
        if (reportDTOList.isEmpty()) {
            throw new BadRequestAlertException("Não é permitido enviar lista vazia", ENTITY_NAME, "inexists");
        }
        reportDTOList.forEach(reportDTO -> {
            reportService.save(reportDTO);
        });

        return ResponseEntity.status(HttpStatus.CREATED).body(reportDTOList);
    }

Código da classe Service:

public Report save(ReportDTO reportDTO) {
        log.debug("Request to save SiraReport : {}", reportDTO);
        if (reportRepository.getByIdProcess(reportDTO.getId_process()) != null) {
            throw new BadRequestAlertException("A new report cannot already have an ID_PROCESS", ENTITY_NAME, "idexists");
        }
        Report report = reportMapper.toEntity(reportDTO);
        return this.reportRepository.save(report);
    } 

Eu só consigo resolver este problema, colocando ifs dos atributos == null na classe service com throw new BadRequestAlertException, porém são muitos atributos. Desta forma terei varios ifs, o que não ficaria legal. O teste para o endpoint, deveria reconhecer as validações e não deixar eu salvar parâmetros nulos.

8 respostas

Oi Tiago,

Pode postar aqui o código completo da sua classe de teste?

Oi Professor, segue a classe teste:

@SpringBootTest(classes = App.class)
@AutoConfigureMockMvc
@WithMockUser
@ActiveProfiles("dev")
public class ReportControllerTest {

    private static final String DEFAULT_ID_PROCESS = "AAAAAAAAAA";

    private static final String DEFAULT_FINAL_STATE = "AAAAAAAAAA";

    private static final String DEFAULT_TAG_ROLLGRINDER = "AAAAAAAAAA";

    private static final String DEFAULT_TAG_ROLL = "AAAAAAAAAA";

    ...+17 atributos

     @Autowired
    private ReportRepository reportRepository;

    @Autowired
    private ReportMapper reportMapper;

    @Autowired
    private EntityManager em;

    @Autowired
    private MockMvc restReportMockMvc;

    private Report report;

     public static Report createEntity(EntityManager em) {
        Report report = new Report()
            .id_process(DEFAULT_ID_PROCESS)
            .final_state(DEFAULT_FINAL_STATE)
            .tag_rollgrinder(DEFAULT_TAG_ROLLGRINDER)
            .tag_roll(DEFAULT_TAG_ROLL)
            . +17 valores defaults
            return report
            }

        @BeforeEach
        public void initTest() {
        report = createEntity(em);
        }

    @Test
    @Transactional
    void shouldComplainAboutListWithObjectsNulls() throws Exception{
        int databaseSizeBeforeCreate = reportRepository.findAll().size();


        List<SReportDTO> dtoList = new ArrayList<>();
        ReportDTO teste1 = new ReportDTO();
        dtoList.add(teste1);    << aqui eu abri o array e adicionei o teste1, para que os atributos já ficassem nulos. O teste não deveria criar objetos nulos.

        restReportMockMvc
            .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(dtoList)))
            .andExpect(status().isCreated());

Acho que você deve substituir essa anotação:

@SpringBootTest(classes = App.class)

Por essa:

@WebMvcTest

Oi Professor,

Troquei, mas os testes nem chegaram a funcionar. Todos falharam e deu o erro:

Description:

Parameter 0 of constructor in br.com.report.web.rest.AccountResource required a bean of type 'br.report.repository.UserRepository' that could not be found.


Action:

Consider defining a bean of type 'br.com.report.repository.UserRepository' in your configuration.

Professor, eu coloquei na minha classe Controller o @Validated pra fazer o Spring reconhecer as validações do bean validation. A princípio funcionou.

@Validated
public class ReportController {

E coloquei também, um manipulador de exception para não dar Erro 500 para o cliente, ao passar parâmetros nulos, assim convertendo para um Erro 400.

 @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
        return new ResponseEntity<>("not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
    }

Agora os testes rodam com o .isBadRequest(), aqueles que deveriam realmente reclamar com erro.

Só que, os que vão ser criados com nulo, ou vazio rodam usando o BadRequest, mas no log ainda aparece o erro Constraint.

javax.validation.ConstraintViolationException: saveListReport.reportDTOList[0].temperatureRollEndTemperature: must not be null

Sabe como faço, para pegar essa Exception e escrever de uma forma melhor, do que aparecer só no Logo desta forma para todos os atributos que vieram null?

Minha classe Controller, para o método Post que estão vindo os erros:

@PostMapping("/report") 
    public ResponseEntity<Object> saveListReport(@RequestBody @Valid List<ReportDTO> reportDTOList) {
        log.debug("REST request to save reports: {}", reportDTOList);
        if (reportDTOList == null || reportDTOList.isEmpty()) {
            throw new BadRequestAlertException("Empty list", ENTITY_NAME, "inexists");
        }

        reportDTOList.forEach(reportDTO -> {
            reportService.save(reportDTO);
        });

        return ResponseEntity.status(HttpStatus.CREATED).body(reportDTOList);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
        return new ResponseEntity<>("not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
    }

Para os testes, que eu coloco uma lista vazia, ele cai no If do throw new BadRequestAlertException e aparece a mensagem que eu coloquei. Contudo, os testes que criam nulo, estão caindo Constraint, mas não consegui colocar alguma mensagem.

Deixa com todas mesmo, conforme demostrado no curso 3 de Spring Boot:

@RunWith(SpringRunner.class) // se tiver usando junit 5 trocar para: @ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
@WithMockUser
@ActiveProfiles("dev")

Assim deveria funcionar normalmente.

Oi professor, a principio deu certo, porém se eu retiro o @Validated da classe Controller ai ele continua enviando os dados nulos.

Agradeço a ajuda professor!

Estou vendo como deixar esta mensagem de erro mais bonita, apesar que ela resulta agora em BadRequest, mas com informações de Constraint.

javax.validation.ConstraintViolationException: saveListReport.reportDTOList[0].temperatureRollEndTemperature: must not be null

Devido o método de manipulação que criei para transformar erro 500 para 400.

 @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
        return new ResponseEntity<>("not valid due to validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
    }

Sabes como posso ajeitar esta mensagem com algum if() throw new?

solução!

Para simplificar o json devolvido no caso de erro 400 relacionado com o bean validation: https://cursos.alura.com.br/course/spring-boot-api-rest/task/55827

Muito obrigado professor. Agradeço a ajuda!