如何在mockmvc中将模拟对象作为JSON发送

时间:2014-06-13 06:55:44

标签: java json unit-testing gson mockito

我想通过内容类型为JSON的MockMvc在控制器中发送一个模拟对象。但是当我尝试序列化模拟时,错误是:

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

有人可以告诉我我错过了什么吗?

5 个答案:

答案 0 :(得分:23)

我建议您创建SomeClass getProperty1()的{​​{1}},以返回getProperty2()SomeClass方法的已知值。根据{{​​1}}的实现方式,您可以直接创建它的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());
}

答案 1 :(得分:7)

即使有可能,将模拟对象提交给JSON转换器也会假设一个单元测试专用于该操作:模拟对象可能有很多属性和方法,远远超出真正的类,序列化可能导致非常奇怪结果。

恕我直言,因为它是一个单元测试,你应该手工编写json序列化字符串。如果您需要控制Gson如何进行序列化,您可以进行其他测试

答案 2 :(得分:1)

我确实找到了一种以下列方式序列化模拟对象的方法:

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

虽然我正在尝试的是USELESS案例,因为json将被剥夺我在test()函数中提供的所有模拟,因此当对象将被重建时,它将没有对它进行模拟的值,并将抛出NullPointerException在第一次使用任何函数/属性时。

编辑:如果你想序列化空值,那么也有一个函数:

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

答案 3 :(得分:1)

我们遇到了类似的问题: 有一个日志语句用于输出序列化为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();

答案 4 :(得分:0)

在测试配置中,您可以使用另一个支持将任何对象序列化为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>