Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add protected method to StandaloneMockMvcBuilder to register additional MVC infrastructure components [SPR-17520] #22052

Closed
spring-issuemaster opened this issue Nov 20, 2018 · 9 comments
Assignees
Milestone

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Nov 20, 2018

Reijhanniel Jearl Campos opened SPR-17520 and commented

We're heavily using Spring MVC, and its awesome Spring MVC test. Our use case is fairly advanced, for we write our custom org.springframework.web.servlet.HandlerMapping and org.springframework.web.servlet.HandlerAdapter . To make use of Spring MVC test however, we needed to duplicate org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder just to allow customization of such beans.

I found a ticket (#16166) that similarly describes the same, albeit only for:

/**
 * Configure factory to create a custom {@link RequestMappingHandlerMapping}.
 * @param factory the factory
 * @since 5.0
 */
public StandaloneMockMvcBuilder setCustomHandlerMapping(Supplier<RequestMappingHandlerMapping> factory) {
   this.handlerMappingFactory = factory;
   return this;
}

We cannot use this method, because we're not relying on RequestMappingHandlerMapping and it doesn't yet have a setter for custom HandlerAdapter s.

Is there a better way to do this?


Affects: 5.1.2

Issue Links:

  • #16166 Provide public methods to register and un-register handler method mappings

Referenced from: commits 2700993

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 20, 2018

Reijhanniel Jearl Campos commented

If the Spring team sees this as a valid use case, I can make a PR for this one! 

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 21, 2018

Rossen Stoyanchev commented

Thanks for the suggestion but the StandaloneMockMvcBuilder is focused on annotated controllers and sets up the infrastructure required for that based on RequestMappingHandlerMapping, RequestMappingHandlerAdapter. It's not clear to me how any other HandlerMapping and HandlerAdapter implementations relate to the StandaloneMockMvcBuilder setup. Please provide a little more context about your situation. How much of the setup in StandaloneMockMvcBuilder do you actually use or need?

Perhaps a separate MockMvcBuilder makes more sense to begin with. Also what about using the MockMvcBuilders.webAppContextSetup(..) with a simple configuration class declaring your HandlerMapping and HandlerAdapter?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 23, 2018

Reijhanniel Jearl Campos commented

Hi Rossen Stoyanchev!

Please provide a little more context about your situation. How much of the setup in StandaloneMockMvcBuilder do you actually use or need?

Right. So I guess what I really need is the configured defaults of StubWebApplicationContext. As of the moment, I override StandaloneMockMvcBuilder#initWebAppContext, and reflectively call addBean on the StubWebApplicationContext to register my custom HandlerMapping and HandlerAdapter. Then the usual spring web MockMvc tests.

Also what about using the MockMvcBuilders.webAppContextSetup(..) with a simple configuration class declaring your HandlerMapping and HandlerAdapter?

Hmmm. So I tried just now, that approach using StaticWebApplicationContext:

StaticWebApplicationContext context = new StaticWebApplicationContext();
context.setServletContext( new MockServletContext() );
context.registerBean( HandlerMapping.class, () -> customHandlerMapping );
context.registerBean( HandlerAdapter.class, () -> customHandlerAdapter );
mockMvc = MockMvcBuilders
    .webAppContextSetup( context )
    .build();

The above yet doesn't work, but it seems like I have to configure a lot more beans just to get Spring web's defaults?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 26, 2018

Rossen Stoyanchev commented

In your initial comment you wrote that you have your own custom HandlerMapping and HandlerAdapter and if I understood correctly that they do not extend RequestMappingHandlerMapping and RequestMappingHandlerAdapter, right? As most of the options in StandaloneMockMvcBuilder are for initializing RequestMappingHandlerMapping and RequestMappingHandlerAdapter, then many of those wouldn't apply then? 

So if my assumption is correct then I think the best option is to go with your own custom MockMvcBuilder extending from AbstractMockMvcBuilder and using StubWebApplicationContext internally. The only beans you would have to configure, in addition to your custom HandlerMapping and HandlerAdapter are these, which isn't much and unless you need to expose options to customize them, it's literally 4-5 lines of code.

 

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 27, 2018

Reijhanniel Jearl Campos commented

...that they do not extend RequestMappingHandlerMapping and RequestMappingHandlerAdapter...

Correct.

...to go with your own custom MockMvcBuilder extending from AbstractMockMvcBuilder...

Yes and no. So we actually have 2 use cases:

  1. Both custom HandlerMapping and HandlerAdapter, so it does use a custom MockMvcBuilder
  2. A custom HandlerMapping, but it creates a HandlerMethod, to be handled by RequestMappingHandlerAdapter

...and using StubWebApplicationContext internally.

Yes! Although as of the moment, it is package-private, so instead I extended StandaloneMockMvcBuilder and overridden initWebAppContext so that I can reflectively add my custom HandlerMapping and HandlerAdapter.

I created a sample proof-of-concept on how I do it at the moment, just so I'm communicating my use case properly: devcsrj/bug-reports@SPR-17520. I'm not sure where exactly I should put that 4-5 lines of code you mentioned. Would love to hear your thoughts

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 28, 2018

Rossen Stoyanchev commented

Yes! Although as of the moment, it is package-private, so instead I extended StandaloneMockMvcBuilder and overridden initWebAppContext so that I can reflectively add my custom HandlerMapping and HandlerAdapter.

I meant to write StaticWebApplicationContext. Indeed StubWebApplicationContext is private because it's really only for internal use. It doesn't work like a regular ApplicationContext that initializes and manages beans. It's merely to allow Spring MVC to look up the beans it needs to find.

Looking at your sample code, we could probably add a protected method in StandaloneMockMvcBuilder to allow for the registration of additional MVC singleton instances. Something like this:

@Override
protected WebApplicationContext initWebAppContext() {
    // ...
    registerMvcSingletons(wac);
    // ...
}

private void registerMvcSingletons(StubWebApplicationContext wac) {
    // ...
    getExtraMvcSingletons().entrySet()
            .forEach(entry -> wac.addBean(entry.getKey(), entry.getValue()));
}

protected Map<String, Object> getExtraMvcSingletons() {
	return Collections.emptyMap();
}
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 3, 2018

Rossen Stoyanchev commented

Reijhanniel Jearl Campos, any comments from your side?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 4, 2018

Reijhanniel Jearl Campos commented

Slr, for some reason I'm not notified via email.

But yes, any means for my boilerplate code to get reduced and usage more seamless would be nice!

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 12, 2018

Rossen Stoyanchev commented

I've added a protected method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.