英文:
Problem testing a Bean with a List property
问题
@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")
    private List<Product> productList;
   
    // getters and setters ...
}
@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 setters ...
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ProductDTO {
    
    private Long id;
    
    private String name;    
    
    private Double price;    
    
    @JsonIgnore
    private Boolean onSale;
    
    private String event;
    
    // constructors, getters, and setters ...
}
public class BrandDTO {
    private String name;
    
    private List<ProductDTO> productList;
    
    // getters and setters ...
}
public interface BrandRepository extends JpaRepository<Brand, Long>{
    
    List<Brand> findAllByOrderByName();
}
public interface BrandService {
    List<Brand> getAllBrands();    
}
@Service
public class BrandServiceImpl implements BrandService {
    
    @Autowired
    BrandRepository myBrandRepo;
    
    @Override
    public List<Brand> getAllBrands(){
        return myBrandRepo.findAllByOrderByName();
    }    
}
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;
    }   
}
@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);
        
    }
    
    @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));
        }
    }
}
Please note that the provided code snippets are translations of the original code you provided, focusing solely on the code itself without any additional content.
英文:
Hi guys so here is my project:
Two entities connected with each other over a @OneToMany relationship:
@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;
}	
}
A simple DAO Class:
public interface BrandRepository extends JpaRepository<Brand, Long>{
List<Brand> findAllByOrderByName();
}
A simple service:
public interface BrandService {
List<Brand> getAllBrands();	
}
@Service
public class BrandServiceImpl implements BrandService {
@Autowired
BrandRepository myBrandRepo;
@Override
public List<Brand> getAllBrands(){
return myBrandRepo.findAllByOrderByName();
}	
}
and a controller which maps Products to ProductDTOs usinf my custom programmed 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 is quite the same as Product entity so I will spare the time ans space writing it down.
So here comes the Problem. I am using Junit to test my Controller having the following class run the test:
@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));
}
}
}
And when I run it I get the following error:
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)
Am I doing something wrong here ? Why am I not getting the List filled with my entities but instead filled with LinkedHashMaps? Is there a solution in order to get a List of Products?
EDIT:
I am also providing here my project dependencies and I added on top of my post also the 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' 
}
答案1
得分: 1
问题在于无法从TreeMap.class推断出对TreeMap<String, List<ProductDTO>>的参数化,而这是在restTemplate.getForObject(...)中发生的。
您应该使用更通用的版本:
restTemplate.exchange(
    "http://localhost:" + port + "/v1/search/get/products/",
    HttpMethod.GET,
    new HttpEntity<>(null, headers),
    new ParameterizedTypeReference<TreeMap<String, List<ProductDTO>>>() {}
)
英文:
The problem is that parameterization of TreeMap<String, List<ProductDTO>> cannot be inferred from TreeMap.class inside restTemplate.getForObject(...)
You'll want to use the more generic version:
restTemplate.exchange(
"http://localhost:"+port+"/v1/search/get/products/",
HttpMethod.GET,
new HttpEntity<>(null, headers),
new ParameterizedTypeReference<TreeMap<String, List<ProductDTO>>() {}
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论