JavaRush /Blog Java /Random-ES /¿Qué es Mapstruct y cómo configurarlo correctamente para ...

¿Qué es Mapstruct y cómo configurarlo correctamente para pruebas unitarias en aplicaciones SpringBoot?

Publicado en el grupo Random-ES

Fondo

¡Hola a todos, mis queridos amigos y lectores! Antes de escribir el artículo, un poco de historia... Recientemente encontré un problema al trabajar con la biblioteca Mapstruct , que describí brevemente en mi canal de Telegram aquí . El problema con la publicación se resolvió en los comentarios, mi colega del proyecto anterior ayudó con esto. Qué es Mapstruct y cómo configurarlo correctamente para pruebas unitarias en aplicaciones SpringBoot.  Parte 1 - 1Después de eso, decidí escribir un artículo sobre este tema, pero, por supuesto, no adoptaremos una visión limitada y primero intentaremos ponernos al día, comprender qué es Mapstruct y por qué es necesario, y usando un ejemplo real Analizar la situación que surgió anteriormente y cómo solucionarla. Por lo tanto, recomiendo encarecidamente hacer todos los cálculos en paralelo con la lectura del artículo para poder experimentar todo en la práctica. Antes de comenzar, suscríbete a mi canal de Telegram , allí recopilo mis actividades, escribo pensamientos sobre el desarrollo en Java y TI en general. ¿Suscrito? ¡Excelente! Bueno, ¡ahora vámonos!

Mapstruct, ¿preguntas frecuentes?

Un generador de código para asignaciones rápidas de beans con seguridad de tipos. Nuestra primera tarea es descubrir qué es Mapstruct y por qué lo necesitamos. En general, puedes leer sobre esto en el sitio web oficial. En la página principal del sitio hay tres respuestas a las preguntas: ¿qué es? ¿Para qué? ¿Cómo? Intentaremos hacer esto también:

¿Lo que es?

Mapstruct es una biblioteca que ayuda a mapear (mapear, en general, eso es lo que siempre dicen: mapear, mapear, etc.) objetos de unas entidades en objetos de otras entidades usando código generado basado en configuraciones que se describen a través de interfaces.

¿Para qué?

En su mayor parte, desarrollamos aplicaciones multicapa (una capa para trabajar con la base de datos, una capa de lógica empresarial, una capa para la interacción de la aplicación con el mundo exterior) y cada capa tiene sus propios objetos para almacenar y procesar datos. . Y estos datos deben transferirse de una capa a otra transfiriéndolos de una entidad a otra. Para aquellos que no han trabajado con este enfoque, esto puede parecer un poco complicado. Por ejemplo, tenemos una entidad para la base de datos de Estudiantes. Cuando los datos de esta entidad van a la capa de lógica de negocios (servicios), necesitamos transferir los datos de la clase Student a la clase StudentModel. A continuación, después de todas las manipulaciones con la lógica empresarial, los datos deben liberarse al exterior. Y para ello tenemos la clase StudentDto. Por supuesto, necesitamos pasar datos de la clase StudentModel a StudentDto. Escribir a mano cada vez los métodos que se transferirán requiere mucha mano de obra. Además, este es un código adicional en la base del código que debe mantenerse. Puedes cometer un error. Y Mapstruct genera dichos métodos en la etapa de compilación y los almacena en fuentes generadas.

¿Cómo?

Usando anotaciones. Solo necesitamos crear una anotación que tenga una anotación de Mapper principal que le diga a la biblioteca que los métodos en esta interfaz se pueden usar para traducir de un objeto a otro. Como dije anteriormente sobre los estudiantes, en nuestro caso esta será la interfaz StudentMapper, la cual tendrá varios métodos para transferir datos de una capa a otra:
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 estas clases creamos un mapper (en adelante esto es lo que llamaremos la interfaz, que describe lo que queremos transferir y dónde):
@Mapper
public interface StudentMapper {
   StudentModel toModel(StudentDTO dto);
   Student toEntity(StudentModel model);
   StudentModel toModel(Student entity);
   StudentDTO toDto(StudentModel model);
}
La belleza de este enfoque es que si los nombres y tipos de campos son los mismos en diferentes clases (como en nuestro caso), entonces la configuración de Mapstruct es suficiente para generar la implementación necesaria basada en la interfaz StudentMapper en la etapa de compilación, que se traducirá. Entonces ya ha quedado más claro, ¿verdad? Vayamos más allá y usemos un ejemplo real para analizar el trabajo en una aplicación Spring Boot.

Un ejemplo de funcionamiento de Spring Boot y Mapstruct

Lo primero que necesitamos es crear un proyecto Spring Boot y agregarle Mapstruct. Para este caso, tengo una organización en GitHub con plantillas para repositorios y un inicio para Spring Boot es una de ellas. En base a ello, creamos un nuevo proyecto: Qué es Mapstruct y cómo configurarlo correctamente para pruebas unitarias en aplicaciones SpringBoot.  Parte 1 - 2A continuación, obtenemos el proyecto . Sí amigos, denle una estrella al proyecto si lo encontraron útil, así sabré que no estoy haciendo esto en vano. En este proyecto, revelaremos una situación que recibí en el trabajo y que describí en una publicación en mi canal de Telegram . Describiré brevemente la situación para aquellos que no lo saben: cuando escribimos pruebas para mapeadores (es decir, para aquellas implementaciones de interfaz de las que hablamos anteriormente), queremos que las pruebas pasen lo más rápido posible. La opción más simple con los mapeadores es usar la anotación SpringBootTest al ejecutar la prueba, que recogerá todo el ApplicationContext de la aplicación Spring Boot e inyectará el mapeador necesario para la prueba dentro de la prueba. Pero esta opción consume muchos recursos y mucho más tiempo, por lo que no es adecuada para nosotros. Necesitamos escribir una prueba unitaria que simplemente cree el asignador deseado y verifique que sus métodos funcionen exactamente como esperamos. ¿Por qué necesita pruebas para ejecutarse más rápido? Si las pruebas llevan mucho tiempo, se ralentiza todo el proceso de desarrollo. Hasta que las pruebas pasen el nuevo código, este código no puede considerarse correcto y no se someterá a pruebas, lo que significa que no se pondrá en producción y, por lo tanto, el desarrollador no ha completado el trabajo. Al parecer, ¿por qué escribir una prueba para una biblioteca cuyo funcionamiento está fuera de toda duda? Y, sin embargo, necesitamos escribir una prueba, porque estamos probando qué tan correctamente describimos el mapeador y si hace lo que esperamos. En primer lugar, para facilitar nuestro trabajo, agreguemos Lombok a nuestro proyecto agregando otra dependencia a pom.xml:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>
En nuestro proyecto, necesitaremos transferir de clases modelo (que se utilizan para trabajar con lógica empresarial) a clases DTO, que utilizamos para comunicarnos con el mundo exterior. En nuestra versión simplificada, asumiremos que los campos no cambian y nuestros mapeadores serán simples. Pero, si lo desea, sería posible escribir un artículo más detallado sobre cómo trabajar con Mapstruct, cómo configurarlo y cómo aprovechar sus ventajas. Pero bueno, este artículo será bastante largo. Digamos que tenemos un estudiante con una lista de conferencias y profesores a los que asiste. Creemos un paquete modelo . En base a esto, crearemos un modelo simple:
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;
}
sus conferencias
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
y profesores
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Y cree un paquete dto junto al paquete modelo :
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;
}
conferencias
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LectureDTO {

   private Long id;

   private String name;
}
y profesores
package com.github.romankh3.templaterepository.springboot.dto;

import lombok.Data;

@Data
public class LecturerDTO {

   private Long id;

   private String name;
}
Ahora creemos un mapeador que traducirá una colección de modelos de conferencias en una colección de conferencias DTO. Lo primero que debe hacer es agregar Mapstruct al proyecto. Para ello utilizaremos su sitio web oficial , allí se describe todo. Es decir, necesitamos agregar una dependencia y un complemento a nuestra memoria (si tienes dudas sobre qué es una memoria, aquí tienes, Artículo1 y Artículo2 ):
<dependency>
   <groupId>org.mapstruct</groupId>
   <artifactId>mapstruct</artifactId>
   <version>1.4.2.Final</version>
</dependency>
y en la memoria en el bloque <build/>. que aún no hemos tenido:
<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 continuación, creemos un paquete de mapeador junto a dto y model . Según las clases que mostramos anteriormente, necesitarás crear cinco mapeadores más:
  • Mapper LectureModel <-> LectureDTO
  • Lista de asignador<LectureModel> <-> Lista<LectureDTO>
  • Mapper LecturerModel <-> LecturerDTO
  • Lista de asignador<LecturerModel> <-> Lista<LecturerDTO>
  • Mapeador StudentModel <-> StudentDTO
Ir:

ConferenciaMapper

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);
}

LectureListMapper

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);
}

ProfesorMapeador

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);
}

ProfesorListMapper

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);
}

EstudianteMapeador

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);
}
Cabe señalar por separado que en mapeadores nos referimos a otros mapeadores. Esto se hace a través del campo de usos en la anotación Mapper, como se hace en StudentMapper:
@Mapper(componentModel = "spring", uses = {LectureListMapper.class, LecturerListMapper.class})
Aquí utilizamos dos mapeadores para mapear correctamente la lista de conferencias y la lista de profesores. Ahora necesitamos compilar nuestro código y ver qué hay allí y cómo. Esto se puede hacer usando el comando de compilación mvn clean . Pero resultó que al crear implementaciones de Mapstruct de nuestros mapeadores, las implementaciones del mapeador no sobrescribieron los campos. ¿Por qué? Resultó que no era posible recoger la anotación de datos de Lombok. Y había que hacer algo... Por eso, tenemos una nueva sección en el artículo.

Vinculando Lombok y Mapstruct

Después de unos minutos de búsqueda, resultó que necesitábamos conectar Lombok y Mapstruct de cierta manera. Hay información sobre esto en la documentación de Mapstruct . Después de examinar el ejemplo propuesto por los desarrolladores de Mapstruct, actualicemos nuestro pom.xml: agreguemos versiones separadas:
​​<properties>
   <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
   <lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
Agreguemos la dependencia que falta:
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok-mapstruct-binding</artifactId>
   <version>${lombok-mapstruct-binding.version}</version>
</dependency>
Y actualicemos nuestro complemento del compilador para que pueda conectar Lombok y 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>
Después de esto todo debería funcionar. Compilemos nuestro proyecto nuevamente. Pero, ¿dónde puedes encontrar las clases que generó Mapstruct? Están en fuentes generadas: ${projectDir}/target/generated-sources/annotations/ Qué es Mapstruct y cómo configurarlo correctamente para pruebas unitarias en aplicaciones SpringBoot.  Parte 1 - 3 Ahora que estamos preparados para darnos cuenta de mi decepción por la publicación de Mapstruct, intentemos crear pruebas para mapeadores.

Escribimos pruebas para nuestros mapeadores.

Crearé una prueba rápida y sencilla que probaría uno de los mapeadores en el caso de que estemos creando una prueba de integración y no nos preocupemos por el tiempo de finalización:

ConferenciaMapperPrueba

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());
   }
}
Aquí, usando la anotación SpringBootTest, lanzamos todo el contexto de aplicación y desde allí, usando la anotación Autowired, extraemos la clase que necesitamos para las pruebas. Desde el punto de vista de la velocidad y facilidad para redactar un examen, esto es muy bueno. La prueba pasa con éxito, todo está bien. Pero iremos al revés y escribiremos una prueba unitaria para un mapeador, por ejemplo, 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());
   }
}
Dado que las implementaciones que genera Mapstruct están en la misma clase que nuestro proyecto, podemos usarlas fácilmente en nuestras pruebas. Todo se ve genial: sin anotaciones, creamos la clase que necesitamos de la forma más sencilla y listo. Pero cuando ejecutamos la prueba, entenderemos que fallará y habrá una excepción NullPointerException en la consola... Esto se debe a que la implementación del asignador LectureListMapper se ve así:
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;
   }
}
Si miramos el NPE (abreviatura de NullPointerException), lo obtenemos de la variable listeningMapper , que resulta no estar inicializada. Pero en nuestra implementación no tenemos un constructor con el que podamos inicializar la variable. ¡Esta es exactamente la razón por la cual Mapstruct implementó el mapeador de esta manera! En Spring, puedes agregar beans a las clases de varias maneras, puedes inyectarlos a través de un campo junto con la anotación Autowired, como se hizo anteriormente, o puedes inyectarlos a través de un constructor. Me encontré en una situación tan problemática en el trabajo cuando necesitaba optimizar el tiempo de ejecución de la prueba. Pensé que no se podía hacer nada al respecto y derramé mi dolor en mi canal de Telegram. Y luego me ayudaron en los comentarios y dijeron que era posible personalizar la estrategia de inyección. La interfaz Mapper tiene un campo injectionStrategy , que solo acepta el nombre de injectionStrategy , que tiene dos valores: FIELD y CONSTRUCTOR . Ahora, sabiendo esto, agreguemos esta configuración a nuestros mapeadores; la mostraré usando LectureListMapper como ejemplo :
@Mapper(componentModel = "spring", uses = LectureMapper.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface LectureListMapper {
   List<LectureModel> toModelList(List<LectureDTO> dtos);
   List<LectureDTO> toDTOList(List<LectureModel> models);
}
Resalté en negrita la parte que agregué. Agreguemos esta opción para todas las demás y volvamos a compilar el proyecto para que los mapeadores se generen con una nueva línea. Cuando hagamos esto, veamos cómo ha cambiado la implementación del mapeador para LectureListMapper (resaltada en negrita la parte que necesitamos):
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;
   }
}
Y ahora Mapstruct ha implementado la inyección del mapeador a través del constructor. Esto es exactamente lo que estábamos tratando de lograr. Ahora nuestra prueba dejará de compilarse, actualicémosla y obtengamos:
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());
   }
}
Ahora bien, si ejecutamos la prueba, todo funcionará como esperábamos, ya que en LectureListMapperImpl le pasamos el LectureMapper que necesita… ¡Victoria! No es difícil para ustedes, pero estoy contento: Amigos, todo está como siempre, suscríbanse a mi cuenta de GitHub , a mi cuenta de Telegram . Allí publico los resultados de mis actividades, hay cosas realmente útiles) Te invito especialmente a unirte al grupo de discusión del canal de Telegram . Sucede que si alguien tiene una pregunta técnica, allí puede obtener una respuesta. Este formato es interesante para todos, puedes leer quién sabe qué y ganar experiencia.

Conclusión

Como parte de este artículo, nos familiarizamos con un producto tan necesario y de uso frecuente como Mapstruct. Descubrimos qué es, por qué y cómo. Utilizando un ejemplo real, sentimos qué se podía hacer y cómo se podía cambiar. También vimos cómo configurar la inyección de beans a través del constructor, para que fuera posible probar correctamente los mapeadores. Los colegas de Mapstruct permitieron a los usuarios de su producto elegir exactamente cómo inyectar los mapeadores, por lo que sin duda les agradecemos. PERO, a pesar de que Spring recomienda inyectar beans a través del constructor, los chicos de Mapstruct han configurado la inyección a través del campo de forma predeterminada. ¿Porqué es eso? Sin respuesta. Sospecho que puede haber razones que simplemente no conocemos, y es por eso que lo hicieron de esta manera. Y para saberlo, creé una edición de GitHub en su repositorio oficial de productos.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION