【问题标题】:BindingResult.getFieldValue() returning null in test context for a formatted valueBindingResult.getFieldValue() 在测试上下文中为格式化值返回 null
【发布时间】:2015-12-26 18:47:25
【问题描述】:

在 spring mvc 应用程序中,我提交 id 并使用格式化程序将该 id 转换为对象。它在容器中运行良好。

但是在单元测试环境中,我发现了一个问题。

我模拟格式化程序总是返回我的测试值,这很好,它被注入到 ModelAttribute 中。但是在 BindingResult 中,例如对 result.getFieldValue("location") 的调用返回 null,但仅在 MockMvc 上下文中。

这是测试用例:

/**
 * Tests the inventory update for existing inventory records.
 * @throws Exception
 */
@Test
public void testUpdateExistingProductInventory() throws Exception{
    logger.entry();
    VariantInventory oldInventory = new VariantInventory();
    oldInventory.setId(20l);

    Product product = ProductBuilder.buildBasicExisting();
    Location location = new Location();
    location.setId(3l);
    ProductVariant variant = new ProductVariant();
    variant.setId(2l);

    // check the formatter is working
    Mockito.when(mockProductFormatter.parse(((String)Mockito.anyObject()), ((Locale)Mockito.anyObject()))).thenReturn(product);
    Product p = mockProductFormatter.parse("1", null);
    Assert.assertEquals(p, product);

    // check the formatter is working
    Mockito.when(mockLocationFormatter.parse(((String)Mockito.anyObject()), ((Locale)Mockito.anyObject()))).thenReturn(location);
    Location l = mockLocationFormatter.parse("3", null);
    Assert.assertEquals(l, location);

    // check the formatter is working
    Mockito.when(mockVariantFormatter.parse(((String)Mockito.anyObject()), ((Locale)Mockito.anyObject()))).thenReturn(variant);
    ProductVariant pv = mockVariantFormatter.parse("2", null);
    Assert.assertEquals(pv, variant);

    // check the formatter is working
    Mockito.when(mockInventoryFormatter.parse(((String)Mockito.anyObject()), ((Locale)Mockito.anyObject()))).thenReturn(oldInventory);
    VariantInventory v = mockInventoryFormatter.parse("20", null);
    Assert.assertEquals(v, oldInventory);

    this.mockMvc.perform(MockMvcRequestBuilders.post("/ajax/products/update/inventory")
            .param("product", "1")
            .param("variant", "2")
            .param("location", "3")
            .param("status", "ACTIVE")
            .param("quantityOnHand", "30.5")
            .param("lowStockQuantity", "10")
            .param("inventory", "20")
            )
            .andExpect(status().isOk());

    Mockito.verify(mockInventoryService, Mockito.times(1)).updateExisting(Mockito.eq(oldInventory), Mockito.any(VariantInventory.class));

    logger.exit();
}

这是控制器的相对部分:

 @RequestMapping(value = "/ajax/products/update/inventory", method= RequestMethod.POST)
    public @ResponseBody
    AJAXResponse updateProductInventory(@ModelAttribute ProductInventoryFormWrapper formWrapper, BindingResult result,
                                         ModelMap map) {
        logger.entry();
        logger.debug("Getting product data");

        if (!result.hasErrors()) {
            inventoryValidator.validate(formWrapper, result);
        }
}

然后跳过一些项目,这是失败的相关验证,我将location 作为字段传递。

ValidationUtils.rejectIfEmptyOrWhitespace(errors, field, "required.field", new String[]{label});

由于一定是错误,该对象无法验证。

如果我调试控制器,我观察到的是:

  1. 对象在 FormWrapper 中,并且属性在那里。
  2. 但是在 BindingResult 对象中,如果我调用 spring 验证代码中调用的 'getFieldValue('location')`,它会返回 null,因此验证器会拒绝该值。

所以由于某种原因,绑定结果没有注册格式化字段或其他东西。请注意,这只发生在单元测试中,而不是容器中。

有人知道怎么解决吗?

快速编辑:

我已经进行了更多调试,但在AbstractPropertyBindingResult 的这段代码中失败了。 value 在调用conversionService 进行转换之前是可以的。我没有下载该方法以外的源代码,所以我无法确切了解它失败的原因,但在 convert 方法的某个地方,它正在从正确的值变为 null。我猜是因为我正在使用 MockObjects,也许它正在调用一些我没有预料到的东西来返回值。

@Override
    protected Object formatFieldValue(String field, Object value) {
        String fixedField = fixedField(field);
        // Try custom editor...
        PropertyEditor customEditor = getCustomEditor(fixedField);
        if (customEditor != null) {
            customEditor.setValue(value);
            String textValue = customEditor.getAsText();
            // If the PropertyEditor returned null, there is no appropriate
            // text representation for this value: only use it if non-null.
            if (textValue != null) {
                return textValue;
            }
        }
        if (this.conversionService != null) {
            // Try custom converter...
            TypeDescriptor fieldDesc = getPropertyAccessor().getPropertyTypeDescriptor(fixedField);
            TypeDescriptor strDesc = TypeDescriptor.valueOf(String.class);
            if (fieldDesc != null && this.conversionService.canConvert(fieldDesc, strDesc)) {
                return this.conversionService.convert(value, fieldDesc, strDesc);
            }
        }
        return value;
    }

【问题讨论】:

    标签: spring-mvc spring-mvc-test


    【解决方案1】:

    好吧,这很难,所以我真的没想到有人会回答。但这是答案。我是对的,在验证中调用了 Mock。所以我不得不向格式化程序(打印)添加一个额外的模拟方法:

    // check the formatter is working
    Mockito.when(mockInventoryFormatter.parse(((String)Mockito.anyObject()), ((Locale)Mockito.anyObject()))).thenReturn(oldInventory);
    // this was added  
    Mockito.when(mockInventoryFormatter.print(Mockito.any(VariantInventory.class), Mockito.any(Locale.class))).thenReturn("20");
    

    【讨论】:

      猜你喜欢
      • 2012-05-09
      • 2020-04-26
      • 1970-01-01
      • 1970-01-01
      • 2021-04-25
      • 2018-10-22
      • 2011-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多