Results 1 to 6 of 6

Thread: Spring MVC 3.2 Unit Test: No BindingResult for attribute "attr"

  1. #1

    Default Spring MVC 3.2 Unit Test: No BindingResult for attribute "attr"

    Hi Folks,

    I am trying to unit test a controller

    Here is the test: -

    Code:
      @Test
      public void testRegisterMemberExists() throws Exception {
        
        doThrow(new MemberExistsServiceException()).when(memberService).registerMember(any(String.class), any(String.class));
        
        this.mockMvc.perform(
            post("/register")
            .param("email", "email@email.com")
            .param("password", "password"))
            .andExpect(status().isOk())   
            .andExpect(model().attributeHasErrors("email"));
        
      }

    Here is the method in question: -

    Code:
    @RequestMapping(value = "/register", method = RequestMethod.POST)
        public String register(@Valid @ModelAttribute(SecurityRegisterModel.KEY) SecurityRegisterModel model, BindingResult result, HttpServletRequest aRequest) {
          
            if (result.hasErrors()){
              return LOGIN;
            }
          
            try{
              memberService.registerMember(model.getEmail(), model.getPassword());
            }catch (MemberExistsServiceException mese){
              result.rejectValue("email", "member.email.exists");
              return LOGIN;
            }
          
            return "redirect:/dashboard";
        }

    Here is the model: -

    Code:
    public class SecurityRegisterModel extends ABaseModel implements Serializable {
    
        private static final long serialVersionUID = -3021884732426352101L;
    
        public final static String KEY = "securityRegisterModel";
    
        @Email
        private String email;
        @Size(min=7)
        private String password;
    
        @Override
        public void reset() {
          setEmail("");
          setPassword("");
        }
    
        @Override
        public String getKey() {
            return KEY;
        }
    
        public String getEmail() {
          return email;
        }
    
        public void setEmail(String email) {
          this.email = email;
        }
    
        public String getPassword() {
          return password;
        }
    
        public void setPassword(String password) {
          this.password = password;
        }
    
        
    
    }
    Through debugging I can see that the result.rejectValue line is called. I expected that to make the assertion "model().attributeHasErrors("email")" pass, but it doesn't.

    I also checked model().attributeExists("email"), but that fails too. I'm not sure what the problem is.

    Any ideas?
    Thanks!
    Ash

  2. #2
    Join Date
    Aug 2006
    Location
    Brooklyn
    Posts
    556

    Default

    You can print all results from the test including all binding errors like so:

    Code:
        this.mockMvc.perform(\post("/register")
            .param("email", "email@email.com")
            .param("password", "password"))
            .andDo(print())
            .andExpect(status().isOk())   
            .andExpect(model().attributeHasErrors("email"))

  3. #3

    Default

    Quote Originally Posted by Rossen Stoyanchev View Post
    You can print all results from the test including all binding errors like so:

    Code:
        this.mockMvc.perform(\post("/register")
            .param("email", "email@email.com")
            .param("password", "password"))
            .andDo(print())
            .andExpect(status().isOk())   
            .andExpect(model().attributeHasErrors("email"))
    Thanks Rossen, that's what I was missing. I'll try it out when I get back to work next week.

    Happy New year
    Ash

  4. #4

    Default

    Hi Rossen, I've got a similar issue.

    So, my test:
    Code:
        public void saveChosenPaymentTypeTest2() throws Exception {
            //no payment type selected
            mockMvc.perform(post("/order/savePayment"))
            		.andDo(print())
                    .andExpect(model().attributeHasErrors("paymentType"))
                    .andExpect(model().errorCount(1))
                    .andExpect(status().isOk())
                    .andExpect(forwardedUrl("order.select.paymentType"));
        }
    Controller method:

    Code:
    @RequestMapping(value = "/savePayment", method = RequestMethod.POST)
    	public ModelAndView saveChosenPaymentType(@Valid SavePaymentTypeForm form, BindingResult bindingResult ,
    			HttpSession session) {
    
            if (bindingResult.hasErrors()) {
            	ModelAndView mav = new ModelAndView("order.select.paymentType");
            	mav.addObject(bindingResult.getTarget());
            	return mav;
            }
    
            ModelAndView mav = new ModelAndView("redirect:/order/confirmation");
            session.setAttribute(ORDER_PAYMENT_TYPE_SESSION_KEY, form.getPaymentType());
    		return mav;
    	}
    The class that should be validated

    Code:
    public class SavePaymentTypeForm {
    	@NotNull(message = "{order.savePayment.error.paymentType.not.selected}")
        private PaymentType paymentType;
    
        public PaymentType getPaymentType() {
            return paymentType;
        }
    
        public void setPaymentType(PaymentType paymentType) {
            this.paymentType = paymentType;
        }
    }
    and results of .andDo(print())
    Code:
    MockHttpServletRequest:
             HTTP Method = POST
             Request URI = /order/savePayment
              Parameters = {}
                 Headers = {}
    
                 Handler:
                    Type = com.euroit.militaryshop.web.controller.OrderWizardController
                  Method = public org.springframework.web.servlet.ModelAndView com.euroit.militaryshop.web.controller.OrderWizardController.saveChosenPaymentType(com.euroit.militaryshop.web.form.SavePaymentTypeForm,org.springframework.validation.BindingResult,javax.servlet.http.HttpSession)
    
      Resolved Exception:
                    Type = null
    
            ModelAndView:
               View name = order.select.paymentType
                    View = null
               Attribute = trolley
                   value = Mock for TrolleyImpl, hashCode: 1397618175
                  errors = []
               Attribute = savePaymentTypeForm
                   value = com.euroit.militaryshop.web.form.SavePaymentTypeForm@75044c61
                  errors = [Field error in object 'savePaymentTypeForm' on field 'paymentType': rejected value [null]; codes [NotNull.savePaymentTypeForm.paymentType,NotNull.paymentType,NotNull.com.euroit.militaryshop.enums.order.PaymentType,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [savePaymentTypeForm.paymentType,paymentType]; arguments []; default message [paymentType]]; default message [{order.savePayment.error.paymentType.not.selected}]]
    
                FlashMap:
    
    MockHttpServletResponse:
                  Status = 200
           Error message = null
                 Headers = {}
            Content type = null
                    Body = 
           Forwarded URL = order.select.paymentType
          Redirected URL = null
                 Cookies = []
    And the test fails with message "No BindingResult for attribute: paymentType
    ". Where can be the problem?

  5. #5

    Default

    The problem is that you are not sending the data in your test. Even though you want to simulate the submission of an empty form, you should send the empty form data in the body of your request. I made the required modifications to your test:

    Code:
      
    public void saveChosenPaymentTypeTest2() throws Exception {
            //no payment type selected
            mockMvc.perform(post("/order/savePayment")
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                    .content(TestUtil.convertObjectToFormUrlEncodedBytes(new SavePaymentForm()))
            )
            		.andDo(print())
                    .andExpect(model().attributeHasErrors("paymentType"))
                    .andExpect(model().errorCount(1))
                    .andExpect(status().isOk())
                    .andExpect(forwardedUrl("order.select.paymentType"));
        }
    Now, this code uses a static convertObjectToFormUrlEncodedBytes() method of the TestUtil class. This is a method which you have to implement as well. Here is an example implementation of that method (If you don't want to add Jackson in your classpath, you have to write your own implementation or just hard code the form url encoded string):

    Code:
    import org.codehaus.jackson.map.ObjectMapper;
    import org.codehaus.jackson.map.annotate.JsonSerialize;
    
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    
    public class TestUtil {
        public static byte[] convertObjectToFormUrlEncodedBytes(Object object) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
    
            Map<String, Object> propertyValues = mapper.convertValue(object, Map.class);
    
            Set<String> propertyNames = propertyValues.keySet();
            Iterator<String> nameIter = propertyNames.iterator();
    
            StringBuilder formUrlEncoded = new StringBuilder();
    
            for (int index=0; index < propertyNames.size(); index++) {
                String currentKey = nameIter.next();
                Object currentValue = propertyValues.get(currentKey);
    
                formUrlEncoded.append(currentKey);
                formUrlEncoded.append("=");
                formUrlEncoded.append(currentValue);
    
                if (nameIter.hasNext()) {
                    formUrlEncoded.append("&");
                }
            }
    
            return formUrlEncoded.toString().getBytes();
        }
    }
    This should solve your problem.
    Last edited by Loke; Jan 25th, 2013 at 03:06 AM.

  6. #6

    Default

    Unfortunately it doesn't help with the same error message.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •