spring - Customized ObjectMapper not used in test -
i using spring framework, version 4.1.6, spring web services , without spring boot. learn framework, writing rest api , testing make sure json response received hitting endpoint correct. specifically, trying adjust objectmapper
's propertynamingstrategy
use "lower case underscores" naming strategy.
i using the method detailed on spring's blog create new objectmapper
, add list of converters. follows:
package com.myproject.config; import com.fasterxml.jackson.databind.propertynamingstrategy; import org.springframework.context.annotation.*; import org.springframework.http.converter.httpmessageconverter; import org.springframework.http.converter.json.jackson2objectmapperbuilder; import org.springframework.http.converter.json.mappingjackson2httpmessageconverter; import org.springframework.web.servlet.config.annotation.enablewebmvc; import org.springframework.web.servlet.config.annotation.webmvcconfigureradapter; import java.util.list; @configuration @enablewebmvc public class webconfig extends webmvcconfigureradapter { @override public void configuremessageconverters(list<httpmessageconverter<?>> converters) { jackson2objectmapperbuilder builder = jacksonbuilder(); converters.add(new mappingjackson2httpmessageconverter(builder.build())); } public jackson2objectmapperbuilder jacksonbuilder() { jackson2objectmapperbuilder builder = new jackson2objectmapperbuilder(); builder.propertynamingstrategy(propertynamingstrategy.camel_case_to_lower_case_with_underscores); return builder; } }
then run following test (using junit, mockmvc, , mockito) verify changes:
package com.myproject.controller; import org.junit.before; import org.junit.test; import org.junit.runner.runwith; import org.mockito.injectmocks; import org.mockito.mock; import org.mockito.mockitoannotations; import org.springframework.http.mediatype; import org.springframework.test.context.contextconfiguration; import org.springframework.test.context.junit4.springjunit4classrunner; import org.springframework.test.context.web.annotationconfigwebcontextloader; import org.springframework.test.context.web.webappconfiguration; import org.springframework.test.web.servlet.mockmvc; import org.springframework.test.web.servlet.mvcresult; import org.springframework.test.web.servlet.setup.mockmvcbuilders; import static org.mockito.mockito.when; import static org.springframework.test.web.servlet.request.mockmvcrequestbuilders.get; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.content; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.status; // along other application imports... @runwith(springjunit4classrunner.class) @webappconfiguration @contextconfiguration(classes = {webconfig.class}, loader = annotationconfigwebcontextloader.class) public class mycontrollertest { @mock private mymanager mymanager; @injectmocks private mycontroller mycontroller; private mockmvc mockmvc; @before public void setup() { mockitoannotations.initmocks(this); this.mockmvc = mockmvcbuilders.standalonesetup(this.mycontroller).build(); } @test public void testmycontrollerwithnameparam() throws exception { myentity expected = new myentity(); string name = "expected"; string title = "expected title"; // set myentity data. expected.setid(1); // random id. expected.setentityname(name); expected.setentitytitle(title) // when mymanager instance asked myentity name parameter, // return expected. when(this.mymanager.read(name)).thenreturn(expected); // assert proper results. mvcresult result = mockmvc.perform( get("/v1/endpoint") .param("name", name)) .andexpect(status().isok()) .andexpect((content().contenttype("application/json;charset=utf-8"))) .andexpect(jsonpath("$.entity_name", is(name)))) .andexpect(jsonpath("$.entity_title", is(title))) .andreturn(); system.out.println(result.getresponse().getcontentasstring()); } }
however, returns response of:
{"id": 1, "entityname": "expected", "entitytitle": "expected title"}
when should get:
{"id": 1, "entity_name": "expected", "entity_title": "expected title"}
i have implemented webapplicationinitializer scans package:
package com.myproject.config; import org.springframework.web.webapplicationinitializer; import org.springframework.web.context.contextloaderlistener; import org.springframework.web.context.support.annotationconfigwebapplicationcontext; import org.springframework.web.servlet.dispatcherservlet; import javax.servlet.servletcontext; import javax.servlet.servletexception; import javax.servlet.servletregistration; public class webappinitializer implements webapplicationinitializer { public void onstartup(servletcontext servletcontext) throws servletexception { annotationconfigwebapplicationcontext ctx = new annotationconfigwebapplicationcontext(); ctx.scan("com.myproject.config"); ctx.setservletcontext(servletcontext); servletregistration.dynamic servlet = servletcontext.addservlet("dispatcher", new dispatcherservlet(ctx)); servlet.setloadonstartup(1); servlet.addmapping("/"); servletcontext.addlistener(new contextloaderlistener(ctx)); } }
using debugger within intellij, can see builder created , added, somewhere down line resulting objectmapper
not used. must missing something, examples i've managed find don't seem mention is! i've tried eliminating @enablewebmvc
, implementing webmvcconfigurationsupport
, using mappingjackson2httpmessageconverter
bean, , setting objectmapper
bean no avail.
any appreciated! please let me know if other files required.
thanks!
edit: doing more digging , found this. in link, author appends setmessageconverters()
before he/she builds mockmvc , works author. doing same worked me well; however, i'm not sure if work in production repositories aren't flushed out yet. when find out submit answer.
edit 2: see answer.
i looked understanding why works way did. reiterate, process of getting customized objectmapper work in test (assuming mockmvc being created standalone) follows:
- create
webconfig
class extendswebmvcconfigureradapter
. - in
webconfig
class, create new@bean
returnsmappingjackson2httpmessageconverter
.mappingjackson2httpmessageconverter
has desired changes applied (in case, passingjackson2objectmapperbuilder
propertynamingstrategy
setcamel_case_to_lower_case_with_underscores
.) - also in
webconfig
class,@override
configuremessageconverters()
, addmappingjackson2httpmessageconverter
(2) list of message converters. - in test file, add
@contextconfiguration(classes = { webconfig.class })
annotation inform test of@bean
. - use
@autowired
inject , access@bean
defined in (2). - in setup of
mockmvc
, use.setmessageconverters()
method , pass injectedmappingjackson2httpmessageconverter
. test use configuration set in (2).
the test file:
package com.myproject.controller; import org.junit.before; import org.junit.test; import org.junit.runner.runwith; import org.mockito.injectmocks; import org.mockito.mock; import org.mockito.mockitoannotations; import org.springframework.http.mediatype; import org.springframework.test.context.contextconfiguration; import org.springframework.test.context.junit4.springjunit4classrunner; import org.springframework.test.context.web.annotationconfigwebcontextloader; import org.springframework.test.context.web.webappconfiguration; import org.springframework.test.web.servlet.mockmvc; import org.springframework.test.web.servlet.mvcresult; import org.springframework.test.web.servlet.setup.mockmvcbuilders; import static org.mockito.mockito.when; import static org.springframework.test.web.servlet.request.mockmvcrequestbuilders.get; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.content; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.status; // along other application imports... @runwith(springjunit4classrunner.class) @webappconfiguration @contextconfiguration(classes = {webconfig.class}) public class mycontrollertest { /** * note converter needs autowired test in order * mockmvc recognize in setup() method. */ @autowired private mappingjackson2httpmessageconverter jackson2httpmessageconverter; @mock private mymanager mymanager; @injectmocks private mycontroller mycontroller; private mockmvc mockmvc; @before public void setup() { mockitoannotations.initmocks(this); this.mockmvc = mockmvcbuilders .standalonesetup(this.mycontroller) .setmessageconverters(this.jackson2httpmessageconverter) // important! .build(); } @test public void testmycontrollerwithnameparam() throws exception { myentity expected = new myentity(); string name = "expected"; string title = "expected title"; // set myentity data. expected.setid(1); // random id. expected.setentityname(name); expected.setentitytitle(title) // when mymanager instance asked myentity name parameter, // return expected. when(this.mymanager.read(name)).thenreturn(expected); // assert proper results. mvcresult result = mockmvc.perform( get("/v1/endpoint") .param("name", name)) .andexpect(status().isok()) .andexpect((content().contenttype("application/json;charset=utf-8"))) .andexpect(jsonpath("$.entity_name", is(name)))) .andexpect(jsonpath("$.entity_title", is(title))) .andreturn(); system.out.println(result.getresponse().getcontentasstring()); } }
and configuration file:
package com.myproject.config; import com.fasterxml.jackson.databind.propertynamingstrategy; import org.springframework.context.annotation.*; import org.springframework.http.converter.httpmessageconverter; import org.springframework.http.converter.json.jackson2objectmapperbuilder; import org.springframework.http.converter.json.mappingjackson2httpmessageconverter; import org.springframework.web.servlet.config.annotation.enablewebmvc; import org.springframework.web.servlet.config.annotation.webmvcconfigureradapter; import java.util.list; @configuration @enablewebmvc public class webconfig extends webmvcconfigureradapter { @override public void configuremessageconverters(list<httpmessageconverter<?>> converters) { converters.add(jackson2httpmessageconverter()); } @bean public mappingjackson2httpmessageconverter jackson2httpmessageconverter() { mappingjackson2httpmessageconverter converter = new mappingjackson2httpmessageconverter(); jackson2objectmapperbuilder builder = this.jacksonbuilder(); converter.setobjectmapper(builder.build()); return converter; } public jackson2objectmapperbuilder jacksonbuilder() { jackson2objectmapperbuilder builder = new jackson2objectmapperbuilder(); builder.propertynamingstrategy(propertynamingstrategy.camel_case_to_lower_case_with_underscores); return builder; } }
deploying generated war file tomcat 7 in xampp shows naming strategy being used correctly. reason believe works way because standalone setup, default set of message converters used unless otherwise specified. can seen in comment setmessageconverters()
function within standalonemockmvcbuilder.java (version 4.1.6, \org\springframework\test\web\servlet\setup\standalonemockmvcbuilder.java
):
/** * set message converters use in argument resolvers , in return value * handlers, support reading and/or writing body of request * , response. if no message converters added list, default * list of converters added instead. */ public standalonemockmvcbuilder setmessageconverters(httpmessageconverter<?>...messageconverters) { this.messageconverters = arrays.aslist(messageconverters); return this; }
therefore, if mockmvc not explicitly told one's changes message converters during building of mockmvc, not use changes.
Comments
Post a Comment