28

In my tests I setup the MockMvc object in the @Before like this

mockMvc = 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?

4 Answers 4

21

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.MockMvcBuilderCustomizerand 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.

Sign up to request clarification or add additional context in comments.

1 Comment

update: 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
17

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.

2 Comments

Thats 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,
You 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
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(); 

1 Comment

This 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.
2
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.

1 Comment

After my test,the default request uri,method does not affect other test requests.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.