Как зарегистрировать Jackson2HalModule вручную для автономного модульного тестирования?
Я старался:
@BeforeClass
public static void setUpClass() {
CurieProvider curieProvider = new DefaultCurieProvider("a", new UriTemplate("a{yey}"));
RelProvider relProvider = new DefaultRelProvider();
ObjectMapper halObjectMapper = JsonUtils.mapper;
halObjectMapper.registerModule(new Jackson2HalModule());
halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));
}
но все равно есть ошибка:
03:26:25.936 [main] ERROR org.soluvas.json.JsonUtils - Cannot serialize id.co.bippo.product.rs.commerceplug.ProductOrServiceImpl as JSON
com.fasterxml.jackson.databind.JsonMappingException: Class org.springframework.hateoas.hal.Jackson2HalModule$HalLinkListSerializer has no default (no arg) constructor
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1042) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:445) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:599) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:92) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:800) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:676) ~[jackson-databind-2.4.3.jar:2.4.3]
at org.soluvas.json.JsonUtils.asJson(JsonUtils.java:54) ~[classes/:na]
at id.co.bippo.product.rs.commerceplug.ProductOrServiceImplTest.productOrService(ProductOrServiceImplTest.java:41) [test-classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_20]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_20]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_20]
at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_20]
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) [junit-4.11.jar:na]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.11.jar:na]
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) [junit-4.11.jar:na]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) [junit-4.11.jar:na]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) [junit-4.11.jar:na]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) [junit-4.11.jar:na]
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) [junit-4.11.jar:na]
at org.junit.runners.ParentRunner.run(ParentRunner.java:309) [junit-4.11.jar:na]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
Caused by: java.lang.IllegalArgumentException: Class org.springframework.hateoas.hal.Jackson2HalModule$HalLinkListSerializer has no default (no arg) constructor
at com.fasterxml.jackson.databind.util.ClassUtil.createInstance(ClassUtil.java:370) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializerInstance(DefaultSerializerProvider.java:474) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BasicSerializerFactory.findSerializerFromAnnotation(BasicSerializerFactory.java:461) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:708) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:557) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:344) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:263) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:222) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:152) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1077) ~[jackson-databind-2.4.3.jar:2.4.3]
at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1037) ~[jackson-databind-2.4.3.jar:2.4.3]
... 31 common frames omitted
2 ответа
Я предполагаю, что вы проводите больше "интеграционного" тестирования, чем модульного тестирования, если вам нужна настройка преобразователей, поскольку контроллеры не возвращают преобразованные ответы. Я могу предложить добавить подобные аннотации в ваш тестовый класс
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
что позволит вам сделать эту инъекцию:
@Autowired
protected WebApplicationContext wac;
Который вы можете затем передать сборщику MockMVC в вашей настройке:
MockMvcBuilders.webAppContextSetup(wac).build()
Application.class будет аннотирован @ComponentScan, который, в свою очередь, получит все ваши конфигурации. Вы также можете передать их явно.
Если вы действительно хотите сгенерировать конвертер явно, вот что у нас есть:
public static HttpMessageConverter<Object> HALMessageConverter(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jackson2HalModule());
//TODO: need to figure out this curie provider stuff...more in production mode
DefaultCurieProvider curieProvider = new DefaultCurieProvider("a", new UriTemplate("http://localhost:8080/myapp/rels/{rel}"));
DefaultRelProvider relProvider = new DefaultRelProvider();
objectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));
MappingJackson2HttpMessageConverter halConverter = new MappingJackson2HttpMessageConverter();
halConverter.setObjectMapper(objectMapper);
halConverter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
return halConverter;
}
и затем вы должны зарегистрировать этот конвертер в экземпляре MVC:
var mockMVC = MockMvcBuilders.standaloneSetup(controller)
.setMessageConverters(HALMessageConverter())
.build();
Похоже, что тип носителя и регистрация, что вы можете пропустить.
Последнее замечание: это исключение совсем другое. Я видел это, когда вы создаете экземпляр ObjectMapper, который использует существующие миксины, но другой сериализатор списка. Это связано с тем, что mixin-ресурс определяет HalListLinkSerializer как сериализатор, но полагается на правильную реализацию. Не уверен, почему вы получаете это... предполагаю, что у вас где-то есть конфигурация аннотации...
Вот что сработало для меня:
import java.io.IOException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.soluvas.json.JsonUtils;
import org.soluvas.json.LowerEnumSerializer;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.RelProvider;
import org.springframework.hateoas.UriTemplate;
import org.springframework.hateoas.core.DefaultRelProvider;
import org.springframework.hateoas.hal.CurieProvider;
import org.springframework.hateoas.hal.DefaultCurieProvider;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.http.MockHttpOutputMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
public class ProductHalJsonTest {
private static final Logger log = LoggerFactory
.getLogger(ProductHalJsonTest.class);
private static MappingJackson2HttpMessageConverter halConverter;
@BeforeClass
public static void setUpClass() {
CurieProvider curieProvider = new DefaultCurieProvider("a", new UriTemplate("a{yey}"));
RelProvider relProvider = new DefaultRelProvider();
ObjectMapper halObjectMapper = JsonUtils.mapper;
LowerEnumSerializer.LOWER = false;
halObjectMapper.registerModule(new Jackson2HalModule());
halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));
halConverter = new MappingJackson2HttpMessageConverter();
halConverter.setObjectMapper(halObjectMapper);
halConverter.setSupportedMediaTypes(ImmutableList.of(MediaTypes.HAL_JSON));
}
@Test
public void product() throws HttpMessageNotWritableException, IOException {
Brand brand = new Brand().withName("Tuneeca");
Product product = new Product()
.withName("T-2081239")
.withBrand(brand)
.addOffer(new Offer().withAvailability(ItemAvailability.IN_STOCK));
// String json = JsonUtils.asJson(product);
final MockHttpOutputMessage msg = new MockHttpOutputMessage();
halConverter.write(product, MediaTypes.HAL_JSON, msg);
String json = msg.getBodyAsString();
log.info("{}", json);
}
}
Тем не менее, я не могу понять, почему я должен создать HttpMessageConverter
когда я просто хочу получить вывод HAL+JSON...