【问题标题】:How to get a JsonProcessingException using Jackson如何使用 Jackson 获取 JsonProcessingException
【发布时间】:2014-11-03 14:13:23
【问题描述】:

可能是一个奇怪的问题,但确实我想在我的测试中实现更多的覆盖率,虽然我针对JsonProcessingException 进行了编码,但我无法创建产生此异常的有效负载,可能是因为杰克逊非常聪明而且将所有内容都转换为字符串,即使对于坏字符串,它也符合 JSON 规范。我的问题是杰克逊很好:)

我基本上想要一个有效载荷,当我运行它时,它会与JsonProcessingException 中断:

String jsonPayload = objectMapper.writeValueAsString(payload);

我尝试过一些类似的方法:

HashMap<String, String> invalidJSONPayload= new HashMap<>();

invalidJSONPayload.put("021",021);
invalidJSONPayload.put("---",021);
invalidJSONPayload.put("~",021);

我对这种类型并不在意,所以请随意推荐另一种。例如,一个空对象会抛出 JsonMappingException,而我也已经捕捉到了。

【问题讨论】:

  • 好吧,你可以模拟一个 ObjectMapper 并让它抛出那个异常,因为 ObjectMapper 不是最终的
  • 这不是在模拟测试本身吗,我想重现一个实际上可以在正在运行的应用程序的上下文中发生的场景。也许一个不存在?如果是这样,为什么例外?感谢您的回答。
  • 好吧,我以为您想测试代码在引发此异常时的行为,这就是我建议这样做的原因
  • 我想这是有道理的,如果没有其他建议,我不妨这样做。我只是想应该有一种方法可以使用某种无法正确解析为 JSON 的随机对象来产生该异常。该死的杰克逊,它是智能解析。
  • 你可以尝试给它一些 XML... :-)

标签: java json jackson


【解决方案1】:

我想做同样的事情,并最终通过使用 Mockito “spy” 函数来完成它,该函数用模拟对象包装了一个真实对象。所有对模拟对象的调用都会被转发到真实对象,除了那些你试图模拟的对象。例如:

ObjectMapper om = Mockito.spy(new ObjectMapper());
Mockito.when( om.writeValueAsString(ErrorObject.class)).thenThrow(new JsonProcessingException("") {});

om 的所有用法都将由底层 ObjectMapper 实例处理,直到传入 ErrorObject 的实例,此时将抛出 JsonProcessingException

newJsonProcessingException 被创建为一个匿名类,因为它是一个受保护的类,并且只能实例化一个子类。

【讨论】:

  • 假设有效负载对象具有公共 getter 和 setter 方法,您可以简单地对有效负载对象使用 spy,然后让其中一个 getter 返回 null。有效载荷 payloadSpy = Mockito.spy(payload); Mockito.doReturn(null).when(payloadSpy).getField(); String jsonPayload = objectMapper.writeValueAsString(payloadSpy);
  • 这将抛出 Checked exception is invalid for this method! 并且不起作用
  • JsonProcessingException 构造函数具有保护访问修饰符。所以,不能通过调用new JsonProcessingException("")来创建
【解决方案2】:

根据 Liam 的回答,用循环模拟 toString() 方法也会导致 Jackson 崩溃。

@Test
public void forceJsonParseException() {
    try {
        Object mockItem = mock(Object.class);
        when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
        new ObjectMapper().writeValueAsString(mockItem);
        fail("did not throw JsonProcessingException");
    } catch (JsonProcessingException e) {
        //pass
    }
}

编辑:比这更容易。 Mockito 模拟总是会抛出它。 o.o;;

【讨论】:

  • 为我工作。由于模拟项不可序列化,因此如果将此模拟项用作测试方法的参数,则会引发异常。顺便说一句,您不需要 when 子句。
【解决方案3】:

你可以使用这样的东西:

private static class ClassThatJacksonCannotSerialize {
    private final ClassThatJacksonCannotSerialize self = this;

    @Override
    public String toString() {
        return self.getClass().getName();
   }
}

这导致JsonProcessingException 带有消息Direct self-reference leading to cycle (through reference chain: ClassThatJacksonCannotSerialize["self"])

【讨论】:

  • 在 2.9 中,我无法使用 toString 覆盖进行复制,我必须这样做:private static class WillNotSerialize { public WillNotSerialize getSelf() { return this; } }
【解决方案4】:

对我来说,如果一个类没有 public 字段/方法,writeValueAsString 将抛出一个 JsonMappingException (no serializer found for class...)

private static class ClassThatJacksonCannotSerialize {}

private void forceProcessingException() {
    ObjectMapper mapper = new ObjectMapper();
    try {
        return mapper.writeValueAsString(value);
    } 
    catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}

【讨论】:

  • 它有效,谢谢。在我的测试中使用它来覆盖 throw 子句:+1:
【解决方案5】:

按照@Mike.Mathieson 的回答

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

public class JacksonTest {

    @Test(expected = JsonProcessingException.class)
    // actually throws an InvalidDefinitionException (which extends JsonProcessingException)
    public void encodeThrowsException() throws JsonProcessingException {
        new ObjectMapper().writeValueAsString(new Object());
    }
}

https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/exc/InvalidDefinitionException.html

请注意,如果 ObjectMapper 已配置为禁用 SerializationFeature.FAIL_ON_EMPTY_BEANS,则此测试将不起作用,例如

new ObjectMapper()
    .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
    .writeValueAsString(new Object());

【讨论】:

    【解决方案6】:

    以防万一这可能对某人有所帮助,当我对 JsonProcessingException 进行单元测试时,我不断收到此错误:

    JsonProcessingException has protected access in com.fasterxml.jackson...
    

    这是我的代码

    // error is on the below line
    JsonProcessingException e = new JsonProcessingException("borked");
    
    doThrow(e).when(classMock).methodToMock(any(), any());
    

    我发现我只需要添加“{}”

    JsonProcessingException e = new JsonProcessingException("borked") {};
    

    【讨论】:

      【解决方案7】:

      我之所以回答这个问题是因为我和提问者的目标相同:

      可能是一个奇怪的问题,但我确实想实现一点 更多覆盖我的测试

      恕我直言,Mockito 解决方案更优雅,不会引起误解,但我向一位同事提出挑战,要求找到另一种解决方案,他做到了。这是解决方案:

      @Test
      @DisplayName("Object To Json KO")
      void objectToJsonKOTest() {
          KafkaMessageDTO o = new KafkaMessageDTO() {
              @Override
              public String getAuthCode() {
                  int a = 2/0;
                  return super.getAuthCode();
              }
          };
      
          String s = mapper.writeValueAsString(o);
          Assertions.assertTrue(s.isEmpty());
      }
      

      这是垃圾,高级垃圾:),但它有效,我想与你分享它作为模拟的替代品

      再见

      【讨论】:

      • 这对我有用。模拟 ObjectMapper 是个坏主意,因为我想测试实例化的真实 ObjectMapper,这就是这段代码的作用。但是它不应该抛出异常而不是返回空吗?
      【解决方案8】:

      尝试使用mock(ObjectMapper.class) 模拟总是会导致检查的异常对于此方法无效!因为不可能抛出检查的异常(JsonProcessingException 扩展IOException)。在许多情况下,像其他建议的答案一样创建自引用值对象可能过于复杂,并且看起来像 hack。

      我发现最简单的方法是扩展ObjectMapper,然后在您的测试方法中使用它。您应该将子类传递给 SUT

      @Test
      public void buildJsonSwallowsJsonProcessingException() {
      
          class MyObjectMapper extends ObjectMapper {
              @Override
              public String writeValueAsString(Object value)
                      throws com.fasterxml.jackson.core.JsonProcessingException {
                  throw new com.fasterxml.jackson.core.JsonProcessingException("Forced error") {};
              }
          }
      
          ObjectMapper objectMapper = new MyObjectMapper();
      
          SUTBean sutbean = new SUTBean(objectMapper);
      
          sutbean.testMethod();
      
          assertTrue(expected, actual);
      }
      

      【讨论】:

        【解决方案9】:

        我在 Jackson Github 问题中找到了这个;但它解决了我的问题,我能够抛出 JsonProcessingException。

         @Test
        public void forceJsonParseException() {
        try {
            Object mockItem = mock(Object.class);
            when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
            new ObjectMapper().writeValueAsString(mockItem);
            fail("did not throw JsonProcessingException");
        } catch (JsonProcessingException e) {
            //pass
        }
        }
        

        现在我如何在我的代码中使用它

        需要测试这个方法

        public String geResponse(MyObject myObject) {
        
        try {
          return objectMapper.writeValueAsString(myObject);
        } catch (JsonProcessingException e) {
          log.error("Service response JsonParsing error {} ", e.getMessage());
          return "Validation Service response JsonParsing error {} "+ e.getMessage();
         }
        }
        

        这就是我测试 JsonProcessingException 的方式

        @SneakyThrows
        @Test
        public void testGetValidationResponseNegative() {
            MyObject mockItem = mock(MyObject.class);
            when(mockItem.toString()).thenReturn(mockItem.getClass().getName());
            String vr = geResponse(mockItem);
            assertTrue(!vr.isEmpty());
        }
        

        希望对你有帮助!!!

        【讨论】:

          【解决方案10】:

          在getter中抛出异常来模拟JsonProcessingException。

          public static class TestData {
              public String getEx() throws JsonProcessingException { throw new JsonParseException(null, "test"); }
          }
          
          ObjectMapper().writeValueAsString(new TestData());
          

          【讨论】:

          • 纯代码答案是低质量的答案。在代码中添加一些解释,以进一步指导 OP。
          【解决方案11】:

          如果将两个字段映射到同一个属性,您会得到 JsonProcessingException。

          class InvalidObject {
              @JsonProperty("s")
              private String x = "value1";
              
              @JsonProperty("s")
              private String y = "value2";
          }
          

          异常消息是“表示属性“s”的多个字段:...”

          【讨论】:

            【解决方案12】:

            您可以通过以下代码获取JsonProcessingException

            import com.fasterxml.jackson.databind.ObjectMapper;
            import com.fasterxml.jackson.databind.node.ObjectNode;
            
            public class SomePOJOClass 
            {
                // Lets assume there are your fields here.
            }
            // ...
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode objectNode = new ObjectNode();
            objectNode.put("unknown-field-in-the-class", "and-its-value");
            SomePOJOClass somePOJOClass = objectMapper.treeToValue(objectNode, SomePOJOClass .class);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-07-15
              • 2015-07-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多