Problema testando um feijão com uma propriedade de Lista

Ilias Mertzanidis:

Oi gente assim aqui é o meu projeto:

Duas entidades ligadas entre si por uma @OneToManyrelação:

@Entity
public class Brand {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    @NotNull(message = "Brand can not have empty name.")
    private String name;

    @Column
    private String description;

    @OneToMany(mappedBy="brand"
    , cascade= {CascadeType.DETACH,
            CascadeType.MERGE,
            CascadeType.PERSIST,
            CascadeType.REFRESH},
    fetch = FetchType.EAGER)    
    @OrderBy("price ASC") // order products by price
    private List<Product> productList;

    // getters and setter following here ...
}

@Entity
public class Product {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    @NotNull(message = "Product can not have empty name.")
    private String name;

    @Column
    private Double price;

    @Column
    private Boolean onSale=false;

    @ManyToOne(cascade= {CascadeType.DETACH,
            CascadeType.MERGE,
            CascadeType.PERSIST,
            CascadeType.REFRESH})
    @JoinColumn(name="brand_id", nullable=false)
    private Brand brand;

 // getters and setter following here ...
}

package com.microservices.product.datatranferobject;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;


@JsonInclude(JsonInclude.Include.NON_NULL)
public class ProductDTO {

    private Long id;

    private String name;    

    private Double price;   

    @JsonIgnore // omit this property when exposing data over the API
    private Boolean onSale;

    private String event;

    public ProductDTO() {

    }    

    public ProductDTO(Long id, String name, Double price, String event, Boolean onSale) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.event = event;
        this.onSale=onSale;
    }

    public Boolean getOnSale() {
        return onSale;
    }

    public void setOnSale(Boolean onSale) {
        this.onSale = onSale;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getEvent() {
        return onSale?"ON SALE":null;
    }

    public void setEvent(String event) {
        this.event = event;
    }    

}


public class BrandDTO {
    private String name;

    private List<ProductDTO> productList;

    public BrandDTO() {}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<ProductDTO> getProductList() {
        return productList;
    }

    public void setProductList(List<ProductDTO> productList) {
        this.productList = productList;
    }   
}

Um simples classe DAO:

public interface BrandRepository extends JpaRepository<Brand, Long>{

    List<Brand> findAllByOrderByName();
}

Um serviço simples:

public interface BrandService {

    List<Brand> getAllBrands(); 
}

@Service
public class BrandServiceImpl implements BrandService {

    @Autowired
    BrandRepository myBrandRepo;

    @Override
    public List<Brand> getAllBrands(){
        return myBrandRepo.findAllByOrderByName();
    }   
}

e um controlador que mapeia os produtos para ProductDTOs usinf meu costume programado Mapper:

public class ModelMapper {
    public static BrandDTO makeBrandDTO(Brand myBrand)
    {
        BrandDTO myBrandDTO=new BrandDTO();

        org.modelmapper.ModelMapper myStandardMapper=new org.modelmapper.ModelMapper();

        List<ProductDTO> myProductsList=myBrand.getProductList()
                .stream()
                .map(product->myStandardMapper.map(product, ProductDTO.class))
                .collect(Collectors.toList());
        myBrandDTO.setProductList(myProductsList);

        myBrandDTO.setName(myBrand.getName());

        return myBrandDTO;
    }
}


@RestController
@RequestMapping("v1/search")
public class BrandController {

    @Autowired
    BrandService myBrandService;

    @GetMapping("/get/products/")
    public Map<String, List<ProductDTO>> getAllProductsMap(){

        List<BrandDTO> myList= myBrandService.getAllBrands()
        .stream().map(brand->ModelMapper.makeBrandDTO(brand))
        .collect(Collectors.toList());      

        Map<String, List<ProductDTO>> myMap=new TreeMap<String, List<ProductDTO>> ();

        myList.forEach(brand->myMap.put(brand.getName(), brand.getProductList()));

        return myMap;
    }

}

ProductDTO é exatamente o mesmo que entidade Product por isso vou poupar espaço o tempo ans escrevê-la.

Então, aqui vem o problema. Eu estou usando JUnit para testar o meu controlador ter a seguinte classe executar o teste:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ProductApplication.class,webEnvironment=WebEnvironment.RANDOM_PORT)
public class ControllerLayerTester {

    private static Log myLogger = LogFactory.getLog(ControllerLayerTester.class);

    private static RestTemplate restTemplate;

    private static HttpHeaders headers;

    @LocalServerPort
    private int port;

    @BeforeClass
    public static void runBeforeAllTestMethods() {

        restTemplate = new RestTemplate();
        headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

    }

    /**
     * simple test to verify the given requirements
     */
    @Test
    public void testBrandListIsOrdered() {

        TreeMap<String, List<ProductDTO>> productList=restTemplate.getForObject("http://localhost:"+port+"/v1/search/get/products/",TreeMap.class);

        for (List<ProductDTO> myProductList:productList.values()) {

            myProductList.forEach(product->assertTrue(product.getId()!=null));

        }
    }

}

E quando eu executá-lo eu recebo o seguinte erro:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.microservices.product.datatranferobject.ProductDTO
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at com.microservices.product.ControllerLayerTester.testBrandListIsOrdered(ControllerLayerTester.java:81)

Estou fazendo algo errado aqui? Por que não estou recebendo a lista preenchida com os meus entidades, mas em vez preenchido com LinkedHashMaps? Existe uma solução para obter uma lista de produtos?

EDITAR:

Eu também estou fornecendo aqui minhas dependências do projeto e eu adicionei em cima do meu post também os DTOs:

buildscript {
    ext {
        springBootVersion = '2.1.0.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.microservices'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.boot:spring-boot-devtools")
    compile group: 'org.modelmapper', name: 'modelmapper', version: '2.1.0'
    runtimeOnly('com.h2database:h2')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
    testImplementation 'org.assertj:assertj-core:3.15.0'
    compile group: 'com.google.guava', name: 'guava', version: '23.5-jre'
    compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.7.0'
    compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.7.0' 
}

crizzis:

O problema é que a parametrização de TreeMap<String, List<ProductDTO>>não pode ser inferida a partir de TreeMap.classdentrorestTemplate.getForObject(...)

Você vai querer usar a versão mais genérica:

restTemplate.exchange(
    "http://localhost:"+port+"/v1/search/get/products/",
    HttpMethod.GET,
    new HttpEntity<>(null, headers),
    new ParameterizedTypeReference<TreeMap<String, List<ProductDTO>>() {}
)

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=296770&siteId=1
Recomendado
Clasificación