我想通过内容类型为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());
}
有人可以告诉我我错过了什么吗?
答案 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>