Как правильно издеваться над объектом ObjectMapper

#java #unit-testing #junit #mockito

#java #модульное тестирование #junit #mockito

Вопрос:

Мне трудно издеваться над 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 ?

Комментарии:

1. строка 46 -Аргумент метода thenReturn не ожидает сопоставления. Попробуйте заменить его, скажем, thenReturn(«someJSON»)

2. вы были правы. в этом проблема! Тем не менее, исключение JsonProcessingException не генерируется. У вас есть какие-либо идеи?

3. publicMethod улавливает исключение. Даже если макет выдает исключение, утверждение завершится ошибкой.

4. вы имеете в виду, что я должен перехватить его в privateMethod, чтобы добиться успеха assert? Потому что я пытался поймать его и в частном методе с теми же результатами

5. Я забыл добавить его при копировании кода. publicMethod действительно вызывает исключение JsonProcessingException в подписи. Тем не менее тест проваливается

Ответ №1:

Я не вижу причины для написания теста, который издевается над 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());
    }
  

Я понимаю вашу точку зрения, и я отчасти согласен с этим. Я просто хочу
поймите, почему, даже если я заставляю mapper выбрасывать это
исключение, я не могу утверждать это

Я думаю, причина этого в том, что исключение перехватывается вами. Это причина, по которой это исключение не распространяется на ваш тест.

 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");
        
    }