Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Form posts through MockMVC

I'm writing tests to verify that I can do a generic form post to our API.

I've also added quite some debugging, but I noticed that the data posted by an actual form; (Postman / AngularJS or w/e) Differs from doing a mockMVC test like:

MvcResult response = mockMvc             .perform(post("/some/super/secret/url") //                     .param("someparam1", "somevalue") //                     .param("someparam2", "somevalue") //                                     .contentType(MediaType.APPLICATION_FORM_URLENCODED) //                     .accept(MediaType.APPLICATION_JSON)) //             .andExpect(status().isOk()) //             .andReturn(); 

The config is exactly the same as the config running in production, and such. However When my interceptor logs the content, in real test (not mockMVC) the content is formatted like "someparam1=somevalue&etc=encore"

When I print the mockMVC content I actually seem to have no content, but there are Params in the request, I assume they're added like GET parameters.

Anyone know how to properly test this? I came upon this issue since it seems like our form posts don't seem to be parsed by Spring even though we have the FormHttpMessageConverter added to the servlet context.

like image 385
Mathijs Segers Avatar asked Apr 12 '16 09:04

Mathijs Segers


People also ask

What is MockMvc used for?

MockMvc provides support for Spring MVC testing. It encapsulates all web application beans and makes them available for testing. We'll initialize the mockMvc object in the @BeforeEach annotated method, so that we don't have to initialize it inside every test.

Is MockMvc integration test?

Technically speaking, tests using MockMvc are in the boundaries between unit and integration tests. They aren't unit tests because endpoints are tested in integration with a Mocked MVC container with mocked inputs and dependencies.

Should I use MockMvc?

As said in this article you should use MockMvc when you want to test Server-side of application: Spring MVC Test builds on the mock request and response from spring-test and does not require a running servlet container.


2 Answers

If you have Apache HTTPComponents HttpClient on your classpath, you can do it like this:

    mockMvc.perform(post("/some/super/secret/url")             .contentType(MediaType.APPLICATION_FORM_URLENCODED)             .content(EntityUtils.toString(new UrlEncodedFormEntity(Arrays.asList(                     new BasicNameValuePair("someparam1", "true"),                     new BasicNameValuePair("someparam2", "test")             ))))); 

If you don't have HttpClient, you can do it with a simple helper method that constructs the urlencoded form entity:

    mockMvc.perform(post("/some/super/secret/url")             .contentType(MediaType.APPLICATION_FORM_URLENCODED)             .content(buildUrlEncodedFormEntity(          "someparam1", "value1",           "someparam2", "value2"     )))); 

With this helper function:

private String buildUrlEncodedFormEntity(String... params) {     if( (params.length % 2) > 0 ) {        throw new IllegalArgumentException("Need to give an even number of parameters");     }     StringBuilder result = new StringBuilder();     for (int i=0; i<params.length; i+=2) {         if( i > 0 ) {             result.append('&');         }         try {             result.             append(URLEncoder.encode(params[i], StandardCharsets.UTF_8.name())).             append('=').             append(URLEncoder.encode(params[i+1], StandardCharsets.UTF_8.name()));         }         catch (UnsupportedEncodingException e) {             throw new RuntimeException(e);         }     }     return result.toString();  } 
like image 104
Bastian Voigt Avatar answered Oct 12 '22 19:10

Bastian Voigt


Here is a Kotlin SpringBoot example:

@RunWith(MockitoJUnitRunner::class) class ApiFormControllerTest {    lateinit var mvc: MockMvc    @InjectMocks   lateinit var apiFormController: ApiFormController    @Before   fun setup() {     mvc = MockMvcBuilders.standaloneSetup(apiFormController).setControllerAdvice(ExceptionAdvice()).build()   }    fun MockHttpServletRequestBuilder.withForm(params: Map<String, String>): MockHttpServletRequestBuilder {     this.contentType(MediaType.APPLICATION_FORM_URLENCODED)         .content(             EntityUtils.toString(                 UrlEncodedFormEntity(                     params.entries.toList().map { BasicNameValuePair(it.key, it.value) }                 )             )         )     return this   }    @Test   fun canSubmitValidForm() {     mvc.perform(post("/forms").withForm(mapOf("subject" to "hello")))         .andExpect(status().isOk)   }  } 
like image 21
JackMahoney Avatar answered Oct 12 '22 19:10

JackMahoney