2014-02-19 59 views
1

嗨,我有方法insertOrUpdateProductsToDB(Product product)用于在Broadleaf中使用catalogService执行数据库中的插入操作,目录Service正在db中进行所有保存操作。我的方法预计restClient产品作为参数。通过restClient产品后,我们通过使用ProductConversion类将此产品转换为Broadleafproduct。在产品转换中,仅将设置转换为将其余产品转换为broadleafproduct。现在,我的要求是测试使用此的Mockito的方法,但是当我试图做我的测试方法如何在mockito中测试循环内循环

verify(mainProduct).getAdditionalSkus().add(sku); 
     verify(mainProduct).setProductOptions(productOptionList); 

其失败的末尾添加这两条线段。

当我调试的代码存在于循环内进行循环的方法insertOrUpdateProductsToDB(Product product)和我在这里找到productOption = catalogService.saveProductOption(productOption); productOption即将空,因此请告诉如何测试内部循环,并同发生环路

for (Sku skuWithProductOptions : productConversion.createSkuWithProductOptions(product, mainProduct,productOptionList)) { 
        catalogService.saveSku(skuWithProductOptions); 
       } 

这用同样的方法排队。同时检查我的测试用例是否正确。

类和insertOrUpdateProductsToDB(Product product)方法是测试

import com.admin.exception.AdminGenericException; 
import com.admin.exception.AdminRestException; 
import com.admin.util.helper.ProductConversion; 
import com.admin.wrapper.getproducts.req.ObjectFactory; 
import com.admin.wrapper.getproducts.resp.Product; 
import com.admin.wrapper.getproducts.resp.Response; 
import com.mycompany.rest.service.client.RestClientUtil; 
import com.mycompany.util.constants.ApplicationConstants; 

@Service 
public class GetProductsServiceImpl { 

    private static final Logger logger = Logger.getLogger(GetProductsServiceImpl.class); 

    @Resource(name = "blCatalogService") 
    protected CatalogService catalogService; 

    public void setCatalogService(CatalogService catalogService) { 
     this.catalogService = catalogService; 
    } 

    protected RestClientUtil restClientUtil; 

    public void setRestClientUtil(RestClientUtil restClientUtil) { 
     this.restClientUtil = restClientUtil; 
    } 

    @Value("#{configProperties['salePriceRate']}") 
    private long salePriceRate; 

    public void setRetailPriceRate(long retailPriceRate) { 
     this.retailPriceRate = retailPriceRate; 
    } 

    @Value("#{configProperties['retailPriceRate']}") 
    private long retailPriceRate; 

    public void setSalePriceRate(long salePriceRate) { 
     this.salePriceRate = salePriceRate; 
    } 


    //Insertion/Update DB logic 
    public String insertOrUpdateProductsToDB(Product product) { 
     logger.debug("Start of : insertOrUpdateProductsToDB()"); 
     try { 
      List<String> category = new ArrayList<String>   (Arrays.asList(ApplicationConstants.CATEGORY)); 
      ProductConversion productConversion = new ProductConversion(); 
      List<ProductOption> productOptionList = new ArrayList<ProductOption>(); 
       if (category.contains(product.getCategory().toUpperCase())) { 
        org.broadleafcommerce.core.catalog.domain.Product mainProduct=catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product", "Normal Product")); 
        mainProduct = productConversion.createProduct(mainProduct,product); 
        Sku sku=catalogService.createSku(); 
        mainProduct.setDefaultSku(sku); 
        mainProduct = productConversion.addSkuToProduct(mainProduct, product, salePriceRate,retailPriceRate); 
        for (ProductOption productOption : productConversion.createProductOptions(product, mainProduct)) { 
         productOption.setAllowedValues(productConversion.createProductOptionValues(product,productOption)); 
         productOption = catalogService.saveProductOption(productOption); 
         productOptionList.add(productOption); 
        } 
        sku = catalogService.saveSku(mainProduct.getDefaultSku()); 
        mainProduct.getAdditionalSkus().add(sku); 
        mainProduct.setProductOptions(productOptionList); 
        mainProduct = catalogService.saveProduct(mainProduct); 
        for (Sku skuWithProductOptions : productConversion.createSkuWithProductOptions(product, mainProduct,productOptionList)) { 
         catalogService.saveSku(skuWithProductOptions); 
        } 
       } 
     logger.debug("End of : insertOrUpdateProductsToDB()"); 
     return "Product inserted into DB successfully"; 
     } 
    catch (Exception e) { 
     logger.error("Error:", e); 
     return "Insertion of product into DB Failed "; 
    } 
    } 
//Insertion service for DB 
    public String insertProductsIntoDB(){ 
     logger.debug("Start of : insertProductsIntoDB()"); 
     int insertionCount=0; 
      try{ 
       com.admin.wrapper.getproducts.resp.Response resp = getAvailableProductsFromPBS(); 
       for (Product product : resp.getProducts().getProduct()) { 
        if(catalogService.findProductById(Long.parseLong(product.getId()))==null){ 
         String str=insertOrUpdateProductsToDB(product); 
         if(str.equalsIgnoreCase("Product inserted into DB successfully")){ 
          insertionCount=insertionCount+1; 
         } 
         } 
        } 
       logger.debug(insertionCount+" Products inserted into DB successfully"); 
       logger.debug("End of : insertProductsIntoDB()"); 
       return insertionCount+" Products inserted into DB successfully"; 
      }catch (AdminRestException e) { 
       logger.error("Error:", e); 
       return e.getMessage(); 
      } 
    } 

} 

我的测试用例类和方法

public class GetProductsServiceImplTest { 
    private CatalogService catalogService; 
    private RestClientUtil restClientUtil; 
    private GetProductsServiceImpl getProductsServiceImpl; 
    private org.broadleafcommerce.core.catalog.domain.Product mainProduct; 
    private Sku sku; 
    private ProductOption productOption; 
    private List<ProductOption> productOptionList; 


    @Before 
    public void setUp() throws Exception { 
     catalogService = mock(CatalogService.class); 
     productOptionList=mock(List.class); 
     mainProduct = spy(new ProductImpl()); 
     sku = new SkuImpl(); 
     getProductsServiceImpl = new GetProductsServiceImpl(); 
     getProductsServiceImpl.setCatalogService(catalogService); 
     productOption=mock(ProductOption.class); 
     restClientUtil = new RestClientUtil(); 


    } 

    @Test 
    public void testInsertOrUpdateProductsToDB() { 

    restClientUtil.setSellerCode("1"); 
    restClientUtil.setPbsUrl("http://10.52.165.239:8080/pbs"); 
    getProductsServiceImpl.setRestClientUtil(restClientUtil); 
    Response pbsResponse = getProductsServiceImpl 
     .getAvailableProductsFromPBS(); 
     for (Product pbsProduct : pbsResponse.getProducts().getProduct()) { 
      when(catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product","Normal Product"))).thenReturn(mainProduct); 
      when(catalogService.createSku()).thenReturn(sku); 
      when(catalogService.saveProductOption(productOption)).thenReturn(productOption); 
      when(catalogService.saveSku(sku)).thenReturn(sku); 
      when(catalogService.saveProduct(mainProduct)).thenReturn(mainProduct); 
      when(catalogService.saveSku(sku)).thenReturn(sku); 
      getProductsServiceImpl.insertOrUpdateProductsToDB(pbsProduct); 
      verify(mainProduct,times(2)).setDefaultSku(sku); 
      verify(mainProduct).getAdditionalSkus().add(sku); 
      verify(mainProduct).setProductOptions(productOptionList); 

      break;   
     } 
     } 
} 

这是错误在测试

java.lang.NullPointerException 
    at com.admin.api.service.getproducts.test.GetProductsServiceImplTest.testInsertOrUpdateProductsToDB(GetProductsServiceImplTest.java:68) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 

回答

1

我有一些言论可能不会回答你的原始问题。但我希望他们能引导您更好地重构此代码。您所展示的代码示例也不足以指出您的具体问题;这是测试方法中的NPE,所以追踪起来应该不那么困难。

话虽这么说这里就是我想提高

  • 测试代码的点是好奇精雕细琢,在我看来这个代码过度使用的Mockito。总的来说,这段代码看起来太复杂,无法正确测试。我不认为这是编码以下TDD原则(TDD真的很方便,当涉及到测试和设计应用程序

  • 您可能要共同遵循的准则在不超过10行代码一个简单的方法,这通常有助于区分疑虑和识别更简单的代码/意图。如果设计正确(没有泄漏的概念或变量),这些更简单的代码可以更容易地更改和测试。例如,您可能想要提取一种方法,只保存一个Product并仅测试该方法。

  • 什么是更引人注目的是,这个代码看起来还挺程序(即使内部的对象)。并没有真正解释商业词语的意图(好吧,这是关于在DB中保存东西,但出于这个原因,所有这些逻辑,这个原因应该出现在方法名称中)。

  • 测试和的Mockito是怪异的,然后代码不应该是遍历集合存根验证

    for (Product pbsProduct : pbsResponse.getProducts().getProduct()) { 
        when(catalogService.createProduct(new ProductType("org.broadleafcommerce.core.catalog.domain.Product","Normal Product"))).thenReturn(mainProduct); 
        when(catalogService.createSku()).thenReturn(sku); 
        when(catalogService.saveProductOption(productOption)).thenReturn(productOption); 
        when(catalogService.saveSku(sku)).thenReturn(sku); 
        when(catalogService.saveProduct(mainProduct)).thenReturn(mainProduct); 
        when(catalogService.saveSku(sku)).thenReturn(sku); 
        getProductsServiceImpl.insertOrUpdateProductsToDB(pbsProduct); 
        verify(mainProduct,times(2)).setDefaultSku(sku); 
        verify(mainProduct).getAdditionalSkus().add(sku); 
        verify(mainProduct).setProductOptions(productOptionList); 
    
        break; 
    } 
    
  • 在伪代码我会先尝试提取使用给出保存逻辑/ * */然后 BBDD关键字(他们有助于澄清什么需要测试在哪种情况和上下文)。把夹具和断言保持在最低限度,你宁愿处理多种测试方法,而不用多种复杂的测试方法。

    @Test 
    public void ensure_product_is_saved_in_the_catalog() { 
        // given 
        Product a_simple_product = ProductBuilder.simpleProduct().build(); 
        when(catalogService.doSomething(....))).thenReturn(mainProduct); 
    
        // when 
        productsService.saveProduct(product); 
    
        // then 
        verify(catalogService).doSomethingElseWith(mainProduct); 
    } 
    

如果产品数据断言是在你的测试场景相关的,然后写一个测试,实际测试数据(使用JUnit断言,AssertJ,...)。不要嘲笑Product

并且每次测试循序渐进,然后重构,如果需要编保持代码管理(如果需要提取单个方法在另一类等)

希望有所帮助。

+0

实际上,被测试的方法不是由我自己写的,而是由其他人完成的,这就是为什么我必须保持它的原样.FYI产品选择,sku必须在保存产品之前保存,之后只能保存产品在db.so可能是这个原因的原因这种方法是这样写的。但问题是,当我进入productOption保存正在发生的循环中,我越来越productOption空归还方法catalogService.saveProductOption(productOption);我无法验证为什么会发生这种情况。 – henrycharles