f-lopes/spring-mvc-test-utils

Possible bug while using with complex type objects? (Missing `Map` support)

Closed this issue · 10 comments

Dear @f-lopes,

I just posted a question at StackOverflow, while experiencing a tricky bug in my tests.
https://stackoverflow.com/q/49782283/9457141

Maybe you have a clue if this could be related to using your little helper and hitting some hidden bug?

Your help would be very much appreciated!

Cheers
Oliver

Actually I think this is indeed related to using your helper... 😄

While comparing the HTTPServletRequest parameters, I notice the following difference:

POST parameters created by your helper

attributes[0]: RQASJc
attributes[1]: ryADf9
defaults: {RQASJc=, ryADf9=}
displayName: svVWw7AUjE
mandatory: {RQASJc=true, ryADf9=true}
multiDoc: true
name: LhzsGo
system: false

POST parameters created by Thymeleaf/Spring combination:

attributes[0]: RQASJc
attributes[1]: ryADf9
defaults[RQASJc]:
defaults[ryADf9]:
displayName: svVWw7AUjE
mandatory[RQASJc]: true
_mandatory[RQASJc]: on
mandatory[ryADf9]: true
_mandatory[ryADf9]: on
multiDoc: true
name: LhzsGo
system: false

When there are optional attributes, the mandatory[name] parameter is missing, but the _mandatory[name]: on is still present, which result in a null entry within the map (which is alright, because I check for this in my validator, etc). The _mandatory[name] keys seem to be ignorable, according to Thymelead + Spring docs

Is this handled differently in JSP than Thymeleaf?

My debug log line I mentioned on StackOverflow seems to be caused by failing to load the property editor in getPropertyEditorFor(Object object):

DEBUG org.springframework.beans.BeanUtils - No property editor [java.util.MapEditor] found for type java.util.Map according to 'Editor' suffix convention

Maybe there should be some code to create proper string representations of type Map? Spring docs at least mention the map[key] conversion.

Dear @poikilotherm,

Thanks for your feedback.

The tool's goal is to create corresponding HTTP request parameters for a given object using reflection to collect the form fields. I have cloned the repository you mentioned in the StackOverflow issue and the given form doesn't declare a "_mandatory" field, hence the tool cannot generate the corresponding HTTP request parameters.

The error (?) you mentioned in the logs is a Spring one (https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java#L510).

As you suggest, the issue is maybe related to the way the controller binds these request parameters to the form object? Adding _mandatory HTTP request parameter makes the test pass.
The conversion is performed here: https://github.com/spring-projects/spring-framework/blob/master/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java#L367

For your information, the next version will support the default property editors registered with Spring MVC.

Dear @f-lopes,

I am not sure I got you correct...

Adding _mandatory HTTP request parameter makes the test pass

Did you just do something like

ResultActions result = mvc.perform(postForm("/admin/doctypes/edit/"+this.valid.getName(), this.valid)
                                                        .param("_mandatory[name]", on)
                                                        );

to make the tests pass?

Because when I use

ResultActions result = mvc.perform(post("/admin/doctypes/edit/{name}", this.valid.getName())
                                         .param("name", this.valid.getName())
                                         .param("displayName", this.valid.getDisplayName())
                                         .param("system", this.valid.getSystem().toString())
                                         .param("multiDoc", this.valid.getMultiDoc().toString())
                                         .param("attributes[0]", this.valid.getAttributes().get(0))
                                         .param("attributes[1]", this.valid.getAttributes().get(1))
                                         .param("mandatory["+this.valid.getAttributes().get(0)+"]", this.valid.getMandatory().get(this.valid.getAttributes().get(0)).toString())
                                         .param("mandatory["+this.valid.getAttributes().get(1)+"]", this.valid.getMandatory().get(this.valid.getAttributes().get(1)).toString())
                                         .param("defaults["+this.valid.getAttributes().get(0)+"]", this.valid.getDefaults().get(this.valid.getAttributes().get(0)))
                                         .param("defaults["+this.valid.getAttributes().get(1)+"]", this.valid.getDefaults().get(this.valid.getAttributes().get(1)))
    );

and thus have no _mandatory[...] inside, the tests start passing, too (as I wrote above, the _mandatory[...] is added by Thymeleaf for being on the safe side).

The main difference IMHO seems to be the representation of the Map - my above code uses the representation mentioned in the (Spring docs about conventions)[https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-beans-conventions].

Could you paste the changed code you came up with here?

Dear @poikilotherm,

I just added a _mandatory field at runtime to the HTTP request parameters before submitting the form.

The support for Map may need some rework and I will integrate it into the next release (work in progress). As you pointed out in #3 (comment), the Map entries need to be handled as an array.

I am willing to contribute if you are interested. Just let me know...

Thanks for your help, I just committed a fix for this issue: 85225de
This commit just add support for simple Map (<String, String>). Don't hesitate to create a pull request if you want to enhance the Map support.

Thank you for your extension of the class @f-lopes!

I'm going to test this later today and either open a PR or provide feedback here :-)

Should the README state some lines about the limitations/restrictions on usable and supported data types?

Your extension works great!

It also works pretty well for other simple object types like Boolean, etc.

What are your plans on releasing?

Thanks for your feedback!

Should the README state some lines about the limitations/restrictions on usable and supported data types?

I think you're right, I will see how I can integrate it into the README. Feel free to open a pull request if you have ideas about it.

I plan to release a new version this week:

  • Will add support for a lot of complex types: the tool will rely on Spring's default property editors.
  • I will also rework the conversion process: the strategy is to always give the user the possibility to override this by providing a custom property editor (in case of a bug).