问题测试具有List属性的Bean。

huangapple 未分类评论46阅读模式
标题翻译

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 = &quot;Brand can not have empty name.&quot;)
    private String name;
	
	@Column
    private String description;
	
	@OneToMany(mappedBy=&quot;brand&quot;
	, cascade= {CascadeType.DETACH,
			CascadeType.MERGE,
			CascadeType.PERSIST,
			CascadeType.REFRESH},
	fetch = FetchType.EAGER)	
	@OrderBy(&quot;price ASC&quot;) // order products by price
	private List&lt;Product&gt; productList;
   
    // getters and setter following here ...
}

@Entity
public class Product {
	
	@Id
	@GeneratedValue
    private Long id;
	
	@Column
	@NotNull(message = &quot;Product can not have empty name.&quot;)
    private String name;
	
	@Column
    private Double price;
	
	@Column
    private Boolean onSale=false;
	
	@ManyToOne(cascade= {CascadeType.DETACH,
			CascadeType.MERGE,
			CascadeType.PERSIST,
			CascadeType.REFRESH})
    @JoinColumn(name=&quot;brand_id&quot;, 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?&quot;ON SALE&quot;:null;
	}

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


public class BrandDTO {
	private String name;
	
	private List&lt;ProductDTO&gt; productList;
	
	public BrandDTO() {}

	public String getName() {
		return name;
	}

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

	public List&lt;ProductDTO&gt; getProductList() {
		return productList;
	}

	public void setProductList(List&lt;ProductDTO&gt; productList) {
		this.productList = productList;
	}	
}

A simple DAO Class:

public interface BrandRepository extends JpaRepository&lt;Brand, Long&gt;{
	
	List&lt;Brand&gt; findAllByOrderByName();
}

A simple service:

public interface BrandService {

	List&lt;Brand&gt; getAllBrands();	
}

@Service
public class BrandServiceImpl implements BrandService {
	
	@Autowired
	BrandRepository myBrandRepo;
	
	@Override
	public List&lt;Brand&gt; 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&lt;ProductDTO&gt; myProductsList=myBrand.getProductList()
				.stream()
				.map(product-&gt;myStandardMapper.map(product, ProductDTO.class))
				.collect(Collectors.toList());
		myBrandDTO.setProductList(myProductsList);
		
		myBrandDTO.setName(myBrand.getName());
		
        return myBrandDTO;
    }
}


@RestController
@RequestMapping(&quot;v1/search&quot;)
public class BrandController {
	
	@Autowired
	BrandService myBrandService;
	
	@GetMapping(&quot;/get/products/&quot;)
	public Map&lt;String, List&lt;ProductDTO&gt;&gt; getAllProductsMap(){
		
		List&lt;BrandDTO&gt; myList= myBrandService.getAllBrands()
		.stream().map(brand-&gt;ModelMapper.makeBrandDTO(brand))
		.collect(Collectors.toList());		
		
		Map&lt;String, List&lt;ProductDTO&gt;&gt; myMap=new TreeMap&lt;String, List&lt;ProductDTO&gt;&gt; ();
		
		myList.forEach(brand-&gt;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&lt;String, List&lt;ProductDTO&gt;&gt; productList=restTemplate.getForObject(&quot;http://localhost:&quot;+port+&quot;/v1/search/get/products/&quot;,TreeMap.class);
		
		for (List&lt;ProductDTO&gt; myProductList:productList.values()) {
			
			myProductList.forEach(product-&gt;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 = &#39;2.1.0.RELEASE&#39;
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath(&quot;org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}&quot;)
	}
}

apply plugin: &#39;java&#39;
apply plugin: &#39;eclipse&#39;
apply plugin: &#39;org.springframework.boot&#39;
apply plugin: &#39;io.spring.dependency-management&#39;

group = &#39;com.microservices&#39;
version = &#39;0.0.1-SNAPSHOT&#39;
sourceCompatibility = 1.8

repositories {
	mavenCentral()
}


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

答案1

得分: 1

问题在于无法从TreeMap.class推断出对TreeMap&lt;String, List&lt;ProductDTO&gt;&gt;的参数化,而这是在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&lt;String, List&lt;ProductDTO&gt;&gt; cannot be inferred from TreeMap.class inside restTemplate.getForObject(...)

You'll want to use the more generic version:

restTemplate.exchange(
    &quot;http://localhost:&quot;+port+&quot;/v1/search/get/products/&quot;,
    HttpMethod.GET,
    new HttpEntity&lt;&gt;(null, headers),
    new ParameterizedTypeReference&lt;TreeMap&lt;String, List&lt;ProductDTO&gt;&gt;() {}
)

huangapple
  • 本文由 发表于 2020年3月17日 03:26:47
  • 转载请务必保留本文链接:https://java.coder-hub.com/60712103.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定