英文:
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>>() {}
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论