JavaRush /Blogue Java /Random-PT /O que é Mapstruct e como configurá-lo corretamente para t...
Roman Beekeeper
Nível 35

O que é Mapstruct e como configurá-lo corretamente para testes unitários em aplicações SpringBoot

Publicado no grupo Random-PT

Fundo

Olá a todos, meus queridos amigos e leitores! Antes de escrevermos o artigo, algumas informações básicas... Recentemente, encontrei um problema ao trabalhar com a biblioteca Mapstruct , que descrevi brevemente em meu canal de telegramas aqui . O problema da postagem foi resolvido nos comentários, meu colega do projeto anterior ajudou nisso. O que é Mapstruct e como configurá-lo corretamente para testes unitários em aplicações SpringBoot.  Parte 1 - 1Depois disso, resolvi escrever um artigo sobre esse tema, mas é claro que não teremos uma visão estreita e tentaremos primeiro nos atualizar, entender o que é Mapstruct e por que ele é necessário, e usando um exemplo real iremos analisar a situação que surgiu anteriormente e como resolvê-la. Portanto, recomendo fortemente fazer todos os cálculos paralelamente à leitura do artigo para vivenciar tudo na prática. Antes de começarmos, inscreva-se no meu canal de telegram , lá coleciono minhas atividades, escrevo reflexões sobre desenvolvimento em Java e TI em geral. Subscrito? Ótimo! Bem, agora vamos!

Mapstruct, perguntas frequentes?

Um gerador de código para mapeamentos rápidos de beans com segurança de tipo. Nossa primeira tarefa é descobrir o que é Mapstruct e por que precisamos dele. Em geral, você pode ler sobre isso no site oficial. Na página principal do site há três respostas para as perguntas: o que é? Para que? Como? Tentaremos fazer isso também:

O que é isso?

Mapstruct é uma biblioteca que ajuda a mapear (mapear, em geral, é o que sempre dizem: mapear, mapear, etc.) objetos de algumas entidades em objetos de outras entidades usando código gerado com base em configurações que são descritas através de interfaces.

Para que?

Na maior parte, desenvolvemos aplicações multicamadas (uma camada para trabalhar com o banco de dados, uma camada de lógica de negócios, uma camada para interação da aplicação com o mundo exterior) e cada camada possui seus próprios objetos para armazenar e processar dados . E esses dados precisam ser transferidos de camada para camada, transferindo-os de uma entidade para outra. Para quem ainda não trabalhou com essa abordagem, isso pode parecer um pouco complicado. Por exemplo, temos uma entidade para o banco de dados Student. Quando os dados desta entidade vão para a camada de lógica de negócios (serviços), precisamos transferir os dados da classe Student para a classe StudentModel. Em seguida, depois de todas as manipulações com a lógica de negócios, os dados precisam ser divulgados externamente. E para isso temos a classe StudentDto. Claro, precisamos passar dados da classe StudentModel para StudentDto. Escrever à mão cada vez que os métodos que serão transferidos exige muito trabalho. Além disso, este é um código extra na base de código que precisa ser mantido. Você pode cometer um erro. E o Mapstruct gera esses métodos na fase de compilação e os armazena nas fontes geradas.

Como?

Usando anotações. Precisamos apenas criar uma anotação que tenha uma anotação principal do Mapper que informe à biblioteca que os métodos nesta interface podem ser usados ​​para traduzir de um objeto para outro. Como falei anteriormente sobre os alunos, no nosso caso esta será a interface StudentMapper, que terá diversos métodos para transferência de dados de uma camada para outra:
public class Student {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentDTO {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}

public class StudentModel {
   private Long id;
   private String firstName;
   private String lastName;
   private Integer age;
}
Para essas classes criamos um mapeador (daqui em diante chamaremos de interface, que descreve o que queremos transferir e para onde):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
A beleza dessa abordagem é que se os nomes e tipos de campos forem iguais em classes diferentes (como no nosso caso), então as configurações do Mapstruct serão suficientes para gerar a implementação necessária com base na interface StudentMapper no estágio de compilação, que irá traduzir. Então já ficou mais claro, né? Vamos além e usar um exemplo real para analisar o trabalho em uma aplicação Spring Boot.

Um exemplo de Spring Boot e Mapstruct funcionando

A primeira coisa que precisamos é criar um projeto Spring Boot e adicionar Mapstruct a ele. Nesse sentido, tenho uma organização no GitHub com templates para repositórios e um start para Spring Boot é um deles. Com base nele, criamos um novo projeto: O que é Mapstruct e como configurá-lo corretamente para testes unitários em aplicações SpringBoot.  Parte 1 - 2A seguir, obtemos o projeto . Sim, amigos, dêem uma estrela ao projeto se vocês acharem útil, para que eu saiba que não estou fazendo isso em vão. Neste projeto vamos revelar uma situação que recebi no trabalho e descrevi em um post no meu canal Telegram . Descreverei brevemente a situação para aqueles que não sabem: quando escrevemos testes para mapeadores (ou seja, para aquelas implementações de interface sobre as quais falamos anteriormente), queremos que os testes passem o mais rápido possível. A opção mais simples com mapeadores é usar a anotação SpringBootTest ao executar o teste, que irá pegar todo o ApplicationContext do aplicativo Spring Boot e injetar o mapeador necessário para o teste dentro do teste. Mas esta opção consome muitos recursos e leva muito mais tempo, por isso não é adequada para nós. Precisamos escrever um teste unitário que simplesmente crie o mapeador desejado e verifique se seus métodos funcionam exatamente como esperamos. Por que você precisa que os testes sejam executados mais rapidamente? Se os testes demorarem muito, todo o processo de desenvolvimento ficará lento. Até que os testes passem no novo código, este código não pode ser considerado correto e não será levado para teste, o que significa que não será levado para produção e o que significa que o desenvolvedor não concluiu o trabalho. Ao que parece, por que escrever um teste para uma biblioteca cujo funcionamento está fora de dúvida? E ainda assim precisamos escrever um teste, porque estamos testando o quão corretamente descrevemos o mapeador e se ele faz o que esperamos. Primeiramente, para facilitar nosso trabalho, vamos adicionar o Lombok ao nosso projeto adicionando outra dependência ao pom.xml:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
Em nosso projeto, precisaremos transferir das classes de modelo (que são usadas para trabalhar com lógica de negócios) para classes DTO, que usamos para nos comunicarmos com o mundo exterior. Em nossa versão simplificada, assumiremos que os campos não mudam e nossos mapeadores serão simples. Mas, se desejar, seria possível escrever um artigo mais detalhado sobre como trabalhar com o Mapstruct, como configurá-lo e como aproveitar seus benefícios. Mas então, já que este artigo será bastante longo. Digamos que temos um aluno com uma lista de palestras e palestrantes que frequenta. Vamos criar um pacote modelo . Com base nisso, criaremos um modelo simples:
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
suas palestras
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
e palestrantes
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
E crie um pacote dto próximo ao pacote model :
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

import java.util.List;

@Data
public class StudentDTO {

   private Long id;

   private String name;

   private List<LectureDTO> lectures;

   private List<LecturerDTO> lecturers;
}
palestras
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
e palestrantes
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Agora vamos criar um mapeador que traduzirá uma coleção de modelos de palestras em uma coleção de palestras DTO. A primeira coisa a fazer é adicionar Mapstruct ao projeto. Para fazer isso, usaremos o site oficial deles , tudo está descrito lá. Ou seja, precisamos adicionar uma dependência e um plugin à nossa memória (se você tiver dúvidas sobre o que é uma memória, aqui está, Article1 e Article2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
e na memória no bloco <build/>. que ainda não tivemos:
<build>
   <plugins>
       <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.5.1</version>
           <configuration>
               <source>1.8</source>
               <target>1.8</target>
               <annotationProcessorPaths>
                   <path>
                       <groupId>org.mapstruct</groupId>
                       <artifactId>mapstruct-processor</artifactId>
                       <version>1.4.2.Final</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
       </plugin>
   </plugins>
</build>
A seguir, vamos criar um pacote mapeador próximo a dto e model . Com base nas classes que mostramos anteriormente, você precisará criar mais cinco mapeadores:
  • Mapeador LectureModel <-> LectureDTO
  • Lista de Mapeadores<LectureModel> <-> Lista<LectureDTO>
  • Mapeador LecturerModel <-> LecturerDTO
  • Lista de mapeadores<LecturerModel> <-> Lista<LecturerDTO>
  • Mapeador StudentModel <-> StudentDTO
Ir:

PalestraMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LecturerDTO dto);
}

PalestraListMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LectureMapper.class)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}

PalestranteMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring")
public interface LectureMapper {
   LectureDTO toDTO(LectureModel model);

   LectureModel toModel(LectureDTO dto);
}

PalestranteListMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LecturerDTO;
import com.github.romankh3.templaterepository.springboot.model.LecturerModel;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring", uses = LecturerMapper.class)
public interface LecturerListMapper {
   List<LecturerModel> toModelList(List<LecturerDTO> dloList);
   List<LecturerDTO> toDTOList(List<LecturerModel> modelList);
}

StudentMapper

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.StudentDTO;
import com.github.romankh3.templaterepository.springboot.model.StudentModel;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
public interface StudentMapper {
   StudentDTO toDTO(StudentModel model);
   StudentModel toModel(StudentDTO dto);
}
Deve-se notar separadamente que em mapeadores nos referimos a outros mapeadores. Isso é feito através do campo usa na anotação do Mapper, assim como é feito no StudentMapper:
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Aqui usamos dois mapeadores para mapear corretamente a lista de palestras e a lista de palestrantes. Agora precisamos compilar nosso código e ver o que existe e como. Isso pode ser feito usando o comando mvn clean compile . Mas, como descobrimos, ao criar implementações Mapstruct de nossos mapeadores, as implementações do mapeador não substituíram os campos. Por que? Acontece que não foi possível obter a anotação de dados do Lombok. E algo precisava ser feito... Portanto, temos uma nova seção no artigo.

Vinculando Lombok e Mapstruct

Após alguns minutos de pesquisa, descobrimos que precisávamos conectar o Lombok e o Mapstruct de uma determinada maneira. Há informações sobre isso na documentação do Mapstruct . Depois de examinar o exemplo proposto pelos desenvolvedores do Mapstruct, vamos atualizar nosso pom.xml: Vamos adicionar versões separadas:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Vamos adicionar a dependência que falta:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
E vamos atualizar nosso plugin compilador para que ele possa conectar Lombok e Mapstruct:
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.5.1</version>
   <configuration>
       <source>1.8</source>
       <target>1.8</target>
       <annotationProcessorPaths>
           <path>
               <groupId>org.mapstruct</groupId>
               <artifactId>mapstruct-processor</artifactId>
               <version>${org.mapstruct.version}</version>
           </path>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>${lombok.version}</version>
           </path>
           <path>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok-mapstruct-binding</artifactId>
               <version>${lombok-mapstruct-binding.version}</version>
           </path>
       </annotationProcessorPaths>
   </configuration>
</plugin>
Depois disso tudo deve dar certo. Vamos compilar nosso projeto novamente. Mas onde você pode encontrar as classes que o Mapstruct gerou? Eles estão em fontes geradas: ${projectDir}/target/generated-sources/annotations/ O que é Mapstruct e como configurá-lo corretamente para testes unitários em aplicações SpringBoot.  Parte 1 - 3 Agora que estamos preparados para perceber minha decepção com a postagem do Mapstruct, vamos tentar criar testes para mapeadores.

Escrevemos testes para nossos mapeadores

Vou criar um teste rápido e simples que testaria um dos mapeadores no caso em que estamos criando um teste de integração e não nos preocupamos com o tempo de conclusão:

PalestraMapperTest

package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class LectureMapperTest {

   @Autowired
   private LectureMapper mapperUnderTest;

   @Test
   void shouldProperlyMapModelToDto() {
       //given
       LectureModel model = new LectureModel();
       model.setId(11L);
       model.setName("lecture name");

       //when
       LectureDTO dto = mapperUnderTest.toDTO(model);

       //then
       Assertions.assertNotNull(dto);
       Assertions.assertEquals(model.getId(), dto.getId());
       Assertions.assertEquals(model.getName(), dto.getName());
   }

   @Test
   void shouldProperlyMapDtoToModel() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(11L);
       dto.setName("lecture name");

       //when
       LectureModel model = mapperUnderTest.toModel(dto);

       //then
       Assertions.assertNotNull(model);
       Assertions.assertEquals(dto.getId(), model.getId());
       Assertions.assertEquals(dto.getName(), model.getName());
   }
}
Aqui, usando a anotação SpringBootTest, lançamos todo o applicationContext e dele, usando a anotação Autowired, extraímos a classe necessária para o teste. Do ponto de vista da rapidez e facilidade de redação de um teste, isso é muito bom. O teste foi aprovado com sucesso, está tudo bem. Mas iremos por outro caminho e escreveremos um teste de unidade para um mapeador, por exemplo, LectureListMapper...
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl();

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Como as implementações geradas pelo Mapstruct estão na mesma classe do nosso projeto, podemos facilmente utilizá-las em nossos testes. Tudo parece ótimo - sem anotações, criamos a classe que precisamos da maneira mais simples e pronto. Mas quando executarmos o teste, entenderemos que ele irá travar e haverá um NullPointerException no console... Isso ocorre porque a implementação do mapeador LectureListMapper se parece com:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T21:46:12+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   @Autowired
   private LectureMapper lectureMapper;

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
Se olharmos para o NPE (abreviação de NullPointerException), nós o obtemos da variável palestraMapper , que não foi inicializada. Mas em nossa implementação não temos um construtor com o qual possamos inicializar a variável. Esta é exatamente a razão pela qual o Mapstruct implementou o mapeador desta forma! No Spring, você pode adicionar beans às classes de várias maneiras, pode injetá-los através de um campo junto com a anotação Autowired, como feito acima, ou pode injetá-los através de um construtor. Eu me encontrei em uma situação muito problemática no trabalho quando precisei otimizar o tempo de execução dos testes. Achei que nada poderia ser feito a respeito e derramei minha dor no meu canal do Telegram. E aí me ajudaram nos comentários e falaram que era possível customizar a estratégia de injeção. A interface do Mapper possui um campo injectorStrategy , que aceita apenas o nome InjectionStrategy , que possui dois valores: FIELD e CONSTRUCTOR . Agora, sabendo disso, vamos adicionar esta configuração aos nossos mapeadores; vou mostrá-la usando LectureListMapper como exemplo :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Destaquei em negrito a parte que adicionei. Vamos adicionar esta opção para todas as outras e recompilar o projeto para que os mapeadores sejam gerados com uma nova linha. Quando fizermos isso, vamos ver como a implementação do mapeador para LectureListMapper mudou (destacada em negrito a parte que precisamos):
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Generated(
   value = "org.mapstruct.ap.MappingProcessor",
   date = "2021-12-09T22:25:37+0300",
   comments = "version: 1.4.2.Final, compiler: javac, environment: Java 15.0.2 (N/A)"
)
@Component
public class LectureListMapperImpl implements LectureListMapper {

   private final LectureMapper lectureMapper;

   @Autowired
   public LectureListMapperImpl(LectureMapper lectureMapper) {

       this.lectureMapper = lectureMapper;
   }

   @Override
   public List<LectureModel> toModelList(List<LectureDTO> dtos) {
       if ( dtos == null ) {
           return null;
       }

       List<LectureModel> list = new ArrayList<LectureModel>( dtos.size() );
       for ( LectureDTO lectureDTO : dtos ) {
           list.add( lectureMapper.toModel( lectureDTO ) );
       }

       return list;
   }

   @Override
   public List<LectureDTO> toDTOList(List<LectureModel> models) {
       if ( models == null ) {
           return null;
       }

       List<LectureDTO> list = new ArrayList<LectureDTO>( models.size() );
       for ( LectureModel lectureModel : models ) {
           list.add( lectureMapper.toDTO( lectureModel ) );
       }

       return list;
   }
}
E agora o Mapstruct implementou a injeção de mapeador por meio do construtor. Isto é exatamente o que estávamos tentando alcançar. Agora nosso teste irá parar de ser compilado, vamos atualizá-lo e obter:
package com.github.romankh3.templaterepository.springboot.mapper;

import com.github.romankh3.templaterepository.springboot.dto.LectureDTO;
import com.github.romankh3.templaterepository.springboot.model.LectureModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Collections;
import java.util.List;

class LectureListMapperTest {

   private final LectureListMapper lectureListMapper = new LectureListMapperImpl(new LectureMapperImpl());

   @Test
   void shouldProperlyMapListDtosToListModels() {
       //given
       LectureDTO dto = new LectureDTO();
       dto.setId(12L);
       dto.setName("I'm BATMAN!");

       List<LectureDTO> dtos = Collections.singletonList(dto);

       //when
       List<LectureModel> models = lectureListMapper.toModelList(dtos);

       //then
       Assertions.assertNotNull(models);
       Assertions.assertEquals(1, models.size());
       Assertions.assertEquals(dto.getId(), models.get(0).getId());
       Assertions.assertEquals(dto.getName(), models.get(0).getName());
   }
}
Agora, se rodarmos o teste, tudo funcionará conforme o esperado, já que no LectureListMapperImpl passamos o LectureMapper que ele precisa... Vitória! Não é difícil para vocês, mas estou satisfeito: amigos, está tudo normal, inscrevam-se na minha conta GitHub , na minha conta Telegram . Lá posto o resultado das minhas atividades, tem coisas realmente úteis) Convido especialmente você a participar do grupo de discussão do canal Telegram . Acontece que se alguém tiver uma dúvida técnica, poderá obter uma resposta aí. Este formato é interessante para todos, você pode ler quem sabe o quê e ganhar experiência.

Conclusão

Como parte deste artigo, conhecemos um produto tão necessário e frequentemente usado como o Mapstruct. Descobrimos o que é, por que e como. Usando um exemplo real, sentimos o que poderia ser feito e como poderia ser mudado. Também vimos como configurar a injeção de beans através do construtor, para que fosse possível testar adequadamente os mapeadores. Colegas do Mapstruct permitiram que os usuários de seu produto escolhessem exatamente como injetar mapeadores, pelo que sem dúvida lhes agradecemos. MAS, apesar do Spring recomendar a injeção de beans por meio do construtor, o pessoal do Mapstruct definiu a injeção por meio do campo por padrão. Por que é que? Nenhuma resposta. Suspeito que possa haver razões que simplesmente não conhecemos, e é por isso que eles fizeram isso dessa maneira. E para descobrir com eles, criei um problema no GitHub em seu repositório oficial de produtos.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION