如何正确地模拟一个ObjectMapper对象

huangapple go评论76阅读模式
英文:

How to correctly mock an ObjectMapper object

问题

我在模拟一个com.fasterxml.jackson.databind.ObjectMapper时遇到了困难。

特别是我想要测试的类如下:

@Service
public class ClassToBeTested {

    private final ObjectMapper mapper;

    public ClassToBeTested(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    public void publicMethod(messageDto dto) throws JsonProcessingException {
        try{
            privateMethod(dto, param, "other string");
        } catch(JsonProcessingException e){
           // do stuff
        }
    }

    private void privateMethod(Object dto, String param, String param2) {
        
        Map<String, Object> attributes = new HashMap<>();

        attributes.putAll(mapper.readValue(mapper.writeValueAsString(dto),
                    new TypeReference<Map<String, Object>>() {
                    }));

        attributes.put("level", "error");
        //other stuff
    }
}

我的测试类如下:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class ClassToBeTestedTest {

    @MockBean
    private ObjectMapper mapper;

    private ClassToBeTested classToBeTestedActor;

    @PostConstruct
    public void initMock() throws JsonProcessingException {
        when(mapper.writeValueAsString(any())).thenReturn(any());
        when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String, Object>>>any()))
            .thenReturn(new HashMap<>());
    }

    @Before
    public void setup() {
        classToBeTestedActor = new ClassToBeTested(mapper);
    }

    @Test
    public void shouldFailJsonParsing() throws JsonProcessingException {
        // Given
        final Dto dto = mock(Dto.class);

        // When
        when(mapper.writeValueAsString(any()))
                .thenThrow(JsonProcessingException.class);

        // Then
        Assertions.assertThrows(JsonProcessingException.class,
                () -> classToBeTestedActor.publicMethod(dto));
    }
}

其目的是触发和测试JsonProcessingException。测试失败是因为出现了InvalidUseOfMatchersException

Invalid use of argument matchers!
2 matchers expected, 3 recorded:
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:47)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
-> at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)

其中第 46 行和第 47 行分别是:

when(mapper.writeValueAsString(any())).thenReturn(any());

when(mapper.readValue(anyString(), Mockito.<TypeReference<Map<String, Object>>>any()))
            .thenReturn(new HashMap<>());

我觉得这对我来说没有任何意义,因为我认为我没有混合使用匹配器和原始值。
我如何正确地模拟ObjectMapper,以便我可以测试JsonProcessingException

英文:

I'm having a hard time mocking a com.fasterxml.jackson.databind.ObjectMapper.

In particular this the class I want to test:

@Service
public class ClassToBeTested {

    private final ObjectMapper mapper;

    public ClassToBeTested(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    public void publicMethod(messageDto dto) throws JsonProcessingException {
        try{
            privateMethod(dto, param, &quot;other string&quot;);
        } catch(JsonProcessingException e){
           // do stuff
        }
    }


    private void privateMethod(Object dto, String param, String param2) {
        
        Map&lt;String, Object&gt; attributes = new HashMap&lt;&gt;();

        attributes.putAll(mapper.readValue(mapper.writeValueAsString(dto),
                    new TypeReference&lt;Map&lt;String, Object&gt;&gt;() {
                    }));

        attributes.put(&quot;level&quot;, &quot;error&quot;);
        //other stuff
    }
}

My test class:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestContextConfiguration.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class ClassToBeTestedTest {

    @MockBean
    private ObjectMapper mapper;

    private ClassToBeTested classToBeTestedActor;

    @PostConstruct
    public void initMock() throws JsonProcessingException {
        when(mapper.writeValueAsString(any())).thenReturn(any());
        when(mapper.readValue(anyString(), Mockito.&lt;TypeReference&lt;Map&lt;String, 
            Object&gt;&gt;&gt;any())).thenReturn(new HashMap&lt;&gt;());
    }

    @Before
    public void setup() {
        classToBeTestedActor = new ClassToBeTested(mapper);
    }

    @Test
    public void shouldFailJsonParsing() throws JsonProcessingException {
        // Given
        final Dto dto = mock(Dto.class);

        // When
        when(mapper.writeValueAsString(any()))
                .thenThrow(JsonProcessingException.class);

        // Then
        Assertions.assertThrows(JsonProcessingException.class,
                () -&gt; classToBeTestedActor.publicMethod(dto));
    }
}

The purpose is to trigger and test the JsonProcessingException. The test fails because of an InvalidUseOfMatchersException:

Invalid use of argument matchers!
2 matchers expected, 3 recorded:
-&gt; at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:47)
-&gt; at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)
-&gt; at com.ocado.crm.service.ClassToBeTestedTest.initMock(ClassToBeTestedTest.java:48)

where line 46 and 47 respectively are:

when(mapper.writeValueAsString(any())).thenReturn(any());

when(mapper.readValue(anyString(), Mockito.&lt;TypeReference&lt;Map&lt;String, Object&gt;&gt;&gt;any()))
                .thenReturn(new HashMap&lt;&gt;());

which makes no sense to me since I think I didn't mixed matchers and raw values.
How can I mock the ObjectMapper correctly so that I can test JsonProcessingException?

答案1

得分: 1

我不明白编写一个模拟对象映射器抛出异常并测试异常是否被抛出的原因。相反,测试应该针对您编写的自定义逻辑。如果您有这样的方法 -

public void publicMethod(messageDto dto)  {
    try{
        privateMethod(dto, param, "other string");
    } catch(JsonProcessingException e){
       // 做一些事情
       dto.setXXX("XXX"); // 测试应该测试这个
    }
}

因此,测试应该测试dto是否被正确设置。

@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
        // 给定
        final Dto dto = new Dto();

        // 当
        when(mapper.writeValueAsString(any()))
                .thenThrow(JsonProcessingException.class);

        // 调用要测试的方法
        classToBeTestedActor.publicMethod(dto);

        // 测试是否调用了预期的模拟方法
        verify(mapper, times(1)).writeValueAsString(any());
        verifyNoMoreInteractions(mapper);

        // 断言结果
        Assert.assertEquals("XXX", dto.getXXX());
}

我理解你的观点,我有点同意。我只是想知道为什么,即使我强制映射器抛出异常,我仍然无法断言它

我认为这是因为异常被您捕获了。这就是为什么异常未传播到您的测试的原因。

public void publicMethod(messageDto dto) throws JsonProcessingException {
        try{
            privateMethod(dto, param, "other string");
        } catch(JsonProcessingException e){
           // 做一些事情
        }
    }

尝试将其更改为 -

public void publicMethod(messageDto dto) throws JsonProcessingException {
        privateMethod(dto, param, "other string");
        
    }
英文:

I don't see the reason behind writing a test which mocks Object Mapper to throw some exception and then test whether the exception was thrown or not. Instead the test should be for the custom logic which is developed by you. If you have a method like this -

public void publicMethod(messageDto dto)  {
    try{
        privateMethod(dto, param, &quot;other string&quot;);
    } catch(JsonProcessingException e){
       // do stuff
       dto.setXXX(&quot;XXX&quot;); // The test should be testing this
    }
}

So, the test should be testing whether the dto is set correctly.

@Test
public void shouldFailJsonParsing() throws JsonProcessingException {
        // Given
        final Dto dto = new Dto();

        // When
        when(mapper.writeValueAsString(any()))
                .thenThrow(JsonProcessingException.class);

        // Invoke the method to be tested
        classToBeTestedActor.publicMethod(dto);

        // Test whether expected mock methods are invoked
        verify(mapper, times(1)).writeValueAsString(any());
        verifyNoMoreInteractions(mapper);

        // Assert results
        Assert.assertEquals(&quot;XXX&quot;, dto.getXXX());
    }

> I get your point and I kind of agree with that. I just want to
> understand why, even if I am forcing the mapper to throw that
> exception, I am unable to assert it

I think the reason for this is that the exception is catched by you. That is the reason, that exception isn't propagated to your test.

public void publicMethod(messageDto dto) throws JsonProcessingException {
        try{
            privateMethod(dto, param, &quot;other string&quot;);
        } catch(JsonProcessingException e){
           // do stuff
        }
    }

Try changing this to -

public void publicMethod(messageDto dto) throws JsonProcessingException {
        privateMethod(dto, param, &quot;other string&quot;);
        
    }

huangapple
  • 本文由 发表于 2020年9月13日 01:47:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/63863227.html
匿名

发表评论

匿名网友

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

确定