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.
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.
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.
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.
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(); }
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) } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With