Как отправить макет объекта как JSON в mockmvc

Я хочу отправить макет объекта в контроллер через MockMvc с типом контента JSON. Но когда я пытаюсь сериализовать макет, ошибка:

java.lang.UnsupportedOperationException: Expecting parameterized type, got interface org.mockito.internal.MockitoInvocationHandler.
 Are you missing the use of TypeToken idiom?

Мой код выглядит следующим образом:

@Test
public void testSomething(){

    String xyz = "";
    Integer i = 10;
    SomeClass inst = mock(SomeClass.class, withSettings().serializable());
    when(inst.getProperty1()).then(xyz);
    when(inst.getProperty2()).then(i);

    Gson gson = new Gson();
    String json = gson.toJson(inst); // this is creating error

    this.mockmvc.perform(put("/someUrl/").contentType(MediaType.JSON).content(json)).andExpect(status().isOk());
}

Может ли кто-нибудь сказать мне, что мне не хватает?

Ответ 1

Я предлагаю вам создать заглушку вашего SomeClass которая возвращает известные значения метода getProperty1() и getProperty2(). В зависимости от того, как реализован SomeClass, вы можете либо создать new экземпляр этого объекта, либо подкласса, либо переопределить некоторые методы, создать анонимный внутренний класс, если это интерфейс и т.д.

@Test
public void testSomething(){

    String xyz = "";
    Integer i = 10;

    // alt 1:
    SomeClass stub = new SomeClass(xyz, i);

    // alt 2: 
    SomeClass stub = new StubSomeClass(xyz, i); // StubSomeClass extends SomeClass

    // alt 3: 
    SomeClass stub = new SomeClass() {
         @Override
         String getProperty1() {
             return xyz;
         }
         @Override
         Integer getProperty2() {
             return i;
         }
    }

    Gson gson = new Gson();
    String json = gson.toJson(stub);

    this.mockmvc.perform(put("/someUrl/")
        .contentType(MediaType.APPLICATION_JSON).content(json))
        .andExpect(status().isOk());
}

Ответ 2

Даже если бы это было возможно, отправка имитационного объекта в JSON-конвертер предполагала бы единичный тест, посвященный этой операции: у mock-объекта может быть много атрибутов и методов, находящихся далеко за пределами реального класса, и сериализация может привести к действительно странному результату.

ИМХО, поскольку это единичный тест, вы должны написать вручную серию сериалов json. И вы можете делать другие тесты, если вам нужно контролировать, как Gson выполняет сериализацию

Ответ 3

Я нашел способ сериализации макетного объекта следующим образом:

Gson gson = new GSon();
String json = gson.toJson(mockedObject, mockedObjectType.class);

Хотя то, что я пытался, было случайным, поскольку json будет лишен всех насмешек, которые я предоставил в функции test(), тем самым, когда объект будет перестроен, он не будет иметь никакого значения насмехаться над ним и будет бросать NullPointerException на первом экземпляр использования любой функции/свойства.

EDIT: Если вы хотите сериализовать нули, для этого есть функция:

Gson gson = new GsonBuilder().serializeNulls().create();

Ответ 4

У нас была аналогичная проблема: для вывода объекта, сериализованного в json, был оператор ведения журнала. И единичный тестовый пример для этой ошибки, потому что gson не смог сериализовать макет объекта. Он был решен с помощью стратегии исключения, которая пропускает сериализацию класса и полей типа Class:

 private final Gson gson = new GsonBuilder()
     .setExclusionStrategies(new ExclusionStrategy() {
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return clazz instanceof Class;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        return field.getDeclaredClass() == Class.class;
    }
}).create();

Ответ 5

В тестовой конфигурации вы можете конвертировать конвертер сообщений по умолчанию в другой конвертер сообщений, который поддерживает сериализацию любого объекта в String.

package com.example;

import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.util.Arrays;

public class MockObjectHttpMessageConverter extends AbstractHttpMessageConverter {


  private final AbstractHttpMessageConverter primaryConverter;

  public MockObjectHttpMessageConverter(AbstractHttpMessageConverter primaryConverter) {
    this.primaryConverter = primaryConverter;
    setSupportedMediaTypes(Arrays.asList(MediaType.ALL));
  }

  @Override
  protected boolean supports(Class clazz) {
    return true;
  }

  @Override
  protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
    throw new UnsupportedOperationException();
  }

  @Override
  protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
    try {
      primaryConverter.write(o, MediaType.ALL, outputMessage);
    } catch (Exception e) {
      IOUtils.write(o.toString(), outputMessage.getBody());
    }
  }
}

В контексте Spring XML:

<mvc:message-converters>
    <bean class="com.example.MockObjectHttpMessageConverter">
        <constructor-arg>
            <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter">
                <property name="gson">
                    <bean class="com.google.gson.Gson" factory-bean="gsonBuilder" factory-method="create"/>
                </property>
            </bean>
        </constructor-arg>
    </bean>
</mvc:message-converters>