如何正确模拟 ObjectMapper 对象

How to correctly mock an ObjectMapper object

我很难嘲笑 com.fasterxml.jackson.databind.ObjectMapper

特别是这个 class 我想测试:

@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
    }
}

我的测试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.<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?

我不明白编写模拟 Object Mapper 抛出一些异常然后测试是否抛出异常的测试背后的原因。相反,测试应该针对您开发的自定义逻辑。如果你有这样的方法 -

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

所以,测试应该是测试dto是否设置正确。

@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("XXX", 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

我觉得是因为异常被你捕获了。这就是该异常不会传播到您的测试的原因。

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

尝试将其更改为 -

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