MockMvc configure a header for all requests
Solution 1
I do not know if it still relevant, but I stumbled over the same problem. We added an API key authentication to a REST api afterwards, and all tests (mainly with @AutoConfigureMockMvc) needed to be adjusted with using a proper API (on top of the new tests, testing that the keys are working).
Spring uses their Customizers and Builders pattern also when creating the MockMvc, like it is done with RestTemplateBuilder and RestTemplateCustomizer.
You are able to create a @Bean/@Component that is a org.springframework.boot.test.autoconfigure.web.servlet.MockMvcBuilderCustomizer
and it will get picked up during the bootstrap process of your @SpringBootTests.
You can then add a parent defaultRequetsBuilders that are merged with the specific RequestBuilders when running the test.
Sample Customizer that adds a header
package foobar;
import org.springframework.boot.test.autoconfigure.web.servlet.MockMvcBuilderCustomizer;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
/**
* Whenever a mockmvc object is autoconfigured, this customizer should be picked up, and a default, usable, working, valid api key is set as
* default authorization header to be applied on all tests if not overwritten.
*
*/
@Component
public class ApiKeyHeaderMockMvcBuilderCustomizer implements MockMvcBuilderCustomizer {
@Override
public void customize(ConfigurableMockMvcBuilder<?> builder) {
// setting the parent (mergeable) default requestbuilder to ConfigurableMockMvcBuilder
// every specifically set value in the requestbuilder used in the test class will have priority over
// the values set in the parent.
// This means, the url will always be replaced, since "any" would not make any sense.
// In case of multi value properties (like headers), existing headers from our default builder they are either merged or appended,
// exactly what we want to achieve
// see https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/web/servlet/MockMvcBuilderCustomizer.html
// and https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/Mergeable.html
RequestBuilder apiKeyRequestBuilder = MockMvcRequestBuilders.get("any")
.header("api-key-header", "apikeyvalue");
builder.defaultRequest(apiKeyRequestBuilder);
}
}
Hope that helps.
Solution 2
How about you make a factory class to start you off with your already decrorated-with-headers request? Since MockHttpServletRequestBuilder
is a builder, you just decorate the request with any of the additional properties (params, content type, etc.) that you need. The builder is designed just for this purpose! For example:
public class MyTestRequestFactory {
public static MockHttpServletRequestBuilder myFactoryRequest(String url) {
return MockMvcRequestBuilders.get(url)
.header("myKey", "myValue")
.header("myKey2", "myValue2");
}
}
Then in your test:
@Test
public void whenITestUrlWithFactoryRequest_thenStatusIsOK() throws Exception {
mockMvc()
.perform(MyTestRequestFactory.myFactoryRequest("/my/test/url"))
.andExpect(status().isOk());
}
@Test
public void whenITestAnotherUrlWithFactoryRequest_thenStatusIsOK() throws Exception {
mockMvc()
.perform(MyTestRequestFactory.myFactoryRequest("/my/test/other/url"))
.andExpect(status().isOk());
}
Each test will call the endpoint with the same headers.
Solution 3
You can write an implementation of javax.servlet.Filter
. In your case, you can add the headers into your request. MockMvcBuilders
has a method to add filters:
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.addFilter(new CustomFilter(), "/*")
.build();
Solution 4
this.mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(new HttpHeaderMockMvcConfigurer()).build();
public class HttpHeaderMockMvcConfigurer extends MockMvcConfigurerAdapter {
@Override
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder, WebApplicationContext cxt) {
builder.defaultRequest(MockMvcRequestBuilders.post("test").header("appId", "aaa"));
return super.beforeMockMvcCreated(builder, cxt);
}
}
Define default request properties that should be merged into all performed requests. In effect this provides a mechanism for defining common initialization for all requests such as the content type, request parameters, session attributes, and any other request property.
isADon
Updated on August 22, 2020Comments
-
isADon over 3 years
In my tests I setup the
MockMvc
object in the@Before
like thismockMvc = MockMvcBuilders.webAppContextSetup(context) .apply(springSecurity()) .build();
In every request I do I always need to send the same headers. Is there a way to configure the headers the
MockMvc
will use globally or per test class? -
isADon almost 6 yearsThats one option. I just don't really like that I need to create a factory method for each of the HTTP request methods even though the all take the same header,
-
Dovmo almost 6 yearsYou can use the same factory method for each request; this would decorate each request with the same headers on each call to the factory method
-
Nic over 5 yearsThis answer would be significantly improved by at least a couple of sentences on how to make that filter. It's a lot more complex than it seems at first.
-
minjay26 over 4 yearsAfter my test,the default request uri,method does not affect other test requests.
-
user2347638 almost 3 yearsWorked for me. Thanks!
-
BigDaddy almost 3 yearsupdate: when I upgraded to Spring 2.4.4 its seems there are now validators, and the builder validates the passed URL, so "any" does not work anymore. You need to insert a valid url, which one does not matter since it will be merged/overwritten during the tests