java - unit test Spring MissingServletRequestParameterException JSON response -
i have post method in spring boot rest controller follows
@requestmapping(value="/post/action/bookmark", method=requestmethod.post) public @responsebody map<string, string> bookmarkpost( @requestparam(value="actiontype",required=true) string actiontype, @requestparam(value="postid",required=true) string postid, @currentuser user user) throws exception{ return service.bookmarkpost(postid, actiontype, user); }
now if test missing parameter in postman 400 http response , json body:
{ "timestamp": "2015-07-20", "status": 400, "error": "bad request", "exception": "org.springframework.web.bind.missingservletrequestparameterexception", "message": "required string parameter 'actiontype' not present", "path": "/post/action/bookmark" }
until it's ok, when try unit test don't json response back
@test public void bookmarkmissingactiontypeparam() throws exception{ // @formatter:off mockmvc.perform( post("/post/action/bookmark") .accept(mediatype.application_json) .param("postid", "55ab8831036437e96e8250b6") ) .andexpect(status().isbadrequest()) .andexpect(jsonpath("$.exception", containsstring("missingservletrequestparameterexception"))); // @formatter:on }
the test fails , produces
java.lang.illegalargumentexception: json can not null or empty
i did .anddo(print()) , found there no body in response
mockhttpservletresponse: status = 400 error message = required string parameter 'actiontype' not present headers = {x-content-type-options=[nosniff], x-xss-protection=[1; mode=block], cache-control=[no-cache, no-store], pragma=[no-cache], expires=[1], x-frame-options=[deny]} content type = null body = forwarded url = null redirected url = null cookies = []
why not getting json response while unit testing controller, receive in manual testing using postman or curl?
edit: i've added @webintegrationtest got same error:
import static org.hamcrest.matchers.containsstring; import static org.springframework.test.web.servlet.request.mockmvcrequestbuilders.post; import static org.springframework.test.web.servlet.result.mockmvcresulthandlers.print; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.jsonpath; import static org.springframework.test.web.servlet.result.mockmvcresultmatchers.status; import org.junit.before; import org.junit.test; import org.junit.runner.runwith; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.test.springapplicationconfiguration; import org.springframework.boot.test.webintegrationtest; import org.springframework.http.mediatype; import org.springframework.security.web.filterchainproxy; import org.springframework.test.context.junit4.springjunit4classrunner; import org.springframework.test.web.servlet.mockmvc; import org.springframework.test.web.servlet.setup.mockmvcbuilders; import org.springframework.web.context.webapplicationcontext; @runwith(springjunit4classrunner.class) @springapplicationconfiguration(classes = restapplication.class) @webintegrationtest public class postcontrollertest { private mockmvc mockmvc; @autowired private webapplicationcontext webapplicationcontext; @autowired private filterchainproxy springsecurityfilterchain; @before public void setup() { mockmvc = mockmvcbuilders.webappcontextsetup(webapplicationcontext) .addfilter(springsecurityfilterchain) .build(); } @test public void bookmarkmissingactiontypeparam() throws exception{ // @formatter:off mockmvc.perform( post("/post/action/bookmark") .accept(mediatype.application_json) .param("postid", "55ab8831036437e96e8250b6") ) .anddo(print()) .andexpect(status().isbadrequest()) .andexpect(jsonpath("$.exception", containsstring("missingservletrequestparameterexception"))); // @formatter:on } }
this because spring boot has auto-configured exception handler org.springframework.boot.autoconfigure.web.basicerrorcontroller
not present in unit tests. way use spring boot testing support related annotations:
@springapplicationconfiguration @webintegrationtest
more details here
update: absolutely right, behavior different in ui vs in test, error pages respond status codes not correctly hooked in non-servlet test environment. improving behavior can bug open spring mvc and/or spring boot.
for now, have workaround simulates behavior of basicerrorcontroller
following way:
@runwith(springjunit4classrunner.class) @springapplicationconfiguration(classes = {restapplication.class, testconfiguration.class}) @webintegrationtest public class postcontrollertest { private mockmvc mockmvc; @autowired private webapplicationcontext webapplicationcontext; @autowired private filterchainproxy springsecurityfilterchain; @before public void setup() { mockmvc = mockmvcbuilders.webappcontextsetup(webapplicationcontext) .addfilter(springsecurityfilterchain) .build(); } @test public void bookmarkmissingactiontypeparam() throws exception{ // @formatter:off mockmvc.perform( post("/post/action/bookmark") .accept(mediatype.application_json) .param("postid", "55ab8831036437e96e8250b6") ) .anddo(print()) .andexpect(status().isbadrequest()) .andexpect(jsonpath("$.exception", containsstring("missingservletrequestparameterexception"))); // @formatter:on } } @configuration public static class testconfiguration { @bean public errorcontroller errorcontroller(errorattributes errorattributes) { return new errorcontroller(errorattributes); } } @controlleradvice class errorcontroller extends basicerrorcontroller { public errorcontroller(errorattributes errorattributes) { super(errorattributes); } @override @exceptionhandler(exception.class) @responsebody public responseentity<map<string, object>> error(httpservletrequest request) { return super.error(request); } }
what doing here adding controlleradvice
handles exception flow , delegates basicerrorcontroller
. atleast make behavior consistent you.
Comments
Post a Comment