Announcement Announcement Module
Collapse
No announcement yet.
help with testing controllers Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • help with testing controllers

    Hi,

    I seem to be getting in a muddle trying to test a controller, and would welcome any suggestions. The controller works OK in the app, but I would like to figure out how to test controllers before I build any more.

    testFormBackingObjectHttpServletRequest() works OK. With testOnSubmitHttpServletRequestHttpServletResponseO bjectBindException I have a problem.

    Code:
    public class MessageReplyControllerTest extends AbstractQueryTestWithAppContext {
    	
    	private MessageReplyController messageReplyController;
    	private MockHttpServletRequest initialRequest = new MockHttpServletRequest("POST", "");
    	private MockHttpServletResponse response = new MockHttpServletResponse();
    	private MockHttpServletRequest onSubmitRequest = new MockHttpServletRequest();
    	
    	
     	
    	protected void setUp() throws Exception {
    		super.setUp();
    		messageReplyController = (MessageReplyController) ctx.getBean("messageReplyController");
    		initialRequest.addParameter("messageID", "2");
    		initialRequest.addParameter("actionMessage", "This is the test action message.");
    		initialRequest.getSession(true).setAttribute("userName", "john");
    		initialRequest.setMethod("GET");
    		
    		// command object
    		MessageReplyInfo messageReplyInfo = new MessageReplyInfo();
    		Message originalMessage = new Message();
    		originalMessage.setToUserName("tash");
    		originalMessage.setSubject("test subject");
    		originalMessage.setBody("This is the test message body.");
    		messageReplyInfo.setOriginalMessage(originalMessage);
    		// add command object to session
    		HttpSession session = onSubmitRequest.getSession(true);
    		session.setAttribute("userName", "john");
    		session.setAttribute("messageReplyInfo", messageReplyInfo);
    		onSubmitRequest.setMethod("POST");		
    		
    		
    	}
    
    	// this works
    	public void testFormBackingObjectHttpServletRequest() {
    		MessageReplyInfo messageReplyInfo = null;
    		try {
    			ModelAndView mv = messageReplyController.handleRequest(initialRequest, response);
    			Map model = mv.getModel();
    			messageReplyInfo = (MessageReplyInfo) model.get("messageReplyInfo");
    		} catch (Exception e) {
    			fail("handleRequest in testFormBackingObjectHttpServletRequest failed."+ e);
    		}
    		assertTrue(messageReplyInfo.getActionMessage().equals("This is the test action message."));
    		assertTrue(messageReplyInfo.getReplyMessage().getSubject().equals("Re: testMessageSubject2"));
    		tableRestorer.restoreTable();		
    	}
    	
    
    	
    	public void testOnSubmitHttpServletRequestHttpServletResponseObjectBindException() {
    		try {
    			messageReplyController.handleRequest(onSubmitRequest, response);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    Watching the debugger, it fails in handleRequestInternal,
    Code:
    if (isSessionForm()) {
    				HttpSession session = request.getSession(false);
    				if (session == null || session.getAttribute(getFormSessionAttributeName(request)) == null) {
    					// Cannot submit a session form if no form object is in the session.
    					return handleInvalidSubmit(request, response);
    				}
    			}
    - getFormSessionAttributeName(request) == null. That is, the command object I have added to the session isn't being found. I guess that I shouldn't be trying to add the command to the session like this - how can I test my onSubmit method?

    Code:
    public class MessageReplyController extends AbstractMessageController {
    
    	protected Object formBackingObject(HttpServletRequest request)
    			throws Exception {
    		int messageID = RequestUtils.getRequiredIntParameter(request,
    				"messageID");
    		String userName = (String) request.getSession(false).getAttribute(
    				"userName");
    		MessageReplyInfo messageReplyInfo = new MessageReplyInfo();
    		Message message = getMessageHandler().getToMessage(messageID, userName);
    		messageReplyInfo.setOriginalMessage(message);
    		String actionMessage = RequestUtils.getStringParameter(request,
    				"actionMessage", "");
    		messageReplyInfo.setActionMessage(actionMessage);
    		return messageReplyInfo;
    	}
    
    	protected ModelAndView onSubmit(HttpServletRequest request,
    			HttpServletResponse response, Object command, BindException errors)
    			throws Exception {
    		MessageReplyInfo messageReplyInfo = (MessageReplyInfo) command;
    		int handlerResponse = getMessageHandler().sendMessage(
    				messageReplyInfo.getReplyMessage());
    		String actionMessage = "";
    		if (handlerResponse > 0)
    			actionMessage = "Your reply to "
    					+ messageReplyInfo.getReplyMessage().getToUserName()
    					+ " has been sent.";
    		else
    			actionMessage = "Sorry, there was a problem sending your message to "
    					+ messageReplyInfo.getReplyMessage().getToUserName()
    					+ ". The administrator has been informed. Please try again later.";
    
    		Map<String, String> model = new HashMap<String, String>&#40;&#41;;
    		model.put&#40;"actionMessage", actionMessage&#41;;
    		return new ModelAndView&#40;getSuccessView&#40;&#41;, model&#41;;
    	&#125;
    &#125;

    The command class:
    Code:
    public class MessageReplyInfo extends AbstractInfo&#123;
       
        private Message replyMessage;
       
    	public void setOriginalMessage&#40;Message originalMessage&#41; &#123;
    		replyMessage = new Message&#40;&#41;;
        	replyMessage.setToUserName&#40;originalMessage.getFromUserName&#40;&#41;&#41;;
        	replyMessage.setFromUserName&#40;originalMessage.getToUserName&#40;&#41;&#41;;
        	replyMessage.setSubject&#40;"Re&#58; " + originalMessage.getSubject&#40;&#41;&#41;;
        	replyMessage.setBody&#40;TextUtils.splitIntoReplyLines&#40;originalMessage.getBody&#40;&#41;&#41;&#41;;	&#125;
    
    	public Message getReplyMessage&#40;&#41; &#123;
    		return replyMessage;
    	&#125;
    &#125;
    ReplyToAMessage.jsp:
    Code:
     <form name="form1" method="POST">
                    <p>To&#58; <c&#58;out value="$&#123;messageReplyInfo.replyMessage.toUserName&#125;"/></p>
                    <p>Subject&#58;
    				<spring&#58;bind path="messageReplyInfo.replyMessage.subject">
    					<FONT color="red">
            			 	<B><c&#58;out value="$&#123;status.errorMessage&#125;"/></B>
            			</FONT>
                        <input name="$&#123;status.expression&#125;" type="text" size="100" maxlength="240" value="<c&#58;out value="$&#123;status.value&#125;"/>
                      " >
    				   </spring&#58;bind>
    				  </p>
                    <p>Message&#58;
    				<spring&#58;bind path="messageReplyInfo.replyMessage.body">
    					<FONT color="red">
            			 	<B><c&#58;out value="$&#123;status.errorMessage&#125;"/></B>
            			</FONT>
                        <textarea name="$&#123;status.expression&#125;" rows="10" cols="80"><c&#58;out value="$&#123;status.value&#125;"/></textarea>
    				</spring&#58;bind>
    				</p>
                    <p>
                      <input type="submit" name="send" value="Send">
    				</p>
                  </form>
    John Pedersen

  • #2
    A couple of points; if you are testing the onSubmit method, then just call onSubmit You only want to unit test *your* code, not springs.

    Second, you are never setting the name of the form, so
    Code:
    getFormSessionAttributeName&#40;request&#41;
    will return null.

    Comment


    • #3
      Thanks for getting back to me on this.

      Call onSubmit directly? onSubmit is protected, not public, so how can I access this directly? That's why I just called handleRequest.


      FormSessionAttributeName is quite a mouthful! I thought

      Code:
      session.setAttribute&#40;"messageReplyInfo", messageReplyInfo&#41;;
      was setting the name of the attribute. From the source code of AbstractFormController:

      Code:
      protected String getFormSessionAttributeName&#40;&#41; &#123;
      		return getClass&#40;&#41;.getName&#40;&#41; + ".FORM." + getCommandName&#40;&#41;;
      	&#125;
      I figured the name was just something used internally in Spring, and I thought I was setting the command name to messageReplyInfo. Obviously I am wrong in this

      Comment


      • #4
        If your unit test is in the same package (which it should be) then you can access protected methods. If it isn't then why

        Code:
        session.setAttribute&#40;"messageReplyInfo", messageReplyInfo&#41;;
        does not set the "name" of the attribute it is storing the attribute "messageReplyInfo" under the attribute "messageReplyInfo". Spring then needs to know this name to retrieve it again.

        Code:
        protected String getFormSessionAttributeName&#40;&#41; &#123;
              return getClass&#40;&#41;.getName&#40;&#41; + ".FORM." + getCommandName&#40;&#41;;
           &#125;
        is a new method on me but it seems to me that if your attribute is stored under "messageReplyInfo" then getFormSessionAttributeName must also return "messageReplyInfo".

        Why are you overriding it anyway, why not just set the commandName to "messageReplyInfo"? If the default implementation

        All of these problems are due to the controller not being wired up properly. I would not worry about it and just test onSetup If the default implementation returns "getClass().getName() + ".FORM." + getCommandName()" then that must be the name you use to set the attribute.

        Comment


        • #5
          Put the tests in the same package - of course - obvious, unless you are me.

          Now simply using :

          messageReplyController.onSubmit(onSubmitRequest, response, messageReplyInfo, null); in a test method does the job.

          Thanks very much for looking into this - I have a bit of refactoring to get on with this afternoon.

          John

          Comment


          • #6
            no probs. At least your are unit testing Don't get me started on the number of colleagues who don't bother "cause they don't have time"

            Comment


            • #7
              When I started using myEclipse, I thought it was great that I could debug even when running in Tomcat, so I got a bit lazy with the tests. Switching to Java 5, Eclipse 3.1 caused all sorts of problems with myEclipse, so I went back to just using Ant, and then I really missed my tests. I had been making good progress, building a class and a test at the same time. Don't know how I was tempted out of the habit!

              Comment


              • #8
                Yeah, I tend to have about 1.5-2 times as much code in unit tests than in implementation

                I also think it explains why I am really quick at writing code. I *know* the code I right works the way it is supposed to. After writing all these "solid" bricks, putting them together is really easy

                Unit tests rock Those green bars are really addictive

                Comment


                • #9
                  If your unit test is in the same package (which it should be) then you can access protected methods. If it isn't then why
                  In my case, I do not want my tests in the same package. When I deploy my web site, I only want to deploy the classes that are going to be used so as not to clutter the server. How then, would I test onSubmit()?

                  Comment


                  • #10
                    Originally posted by Linkbew
                    In my case, I do not want my tests in the same package. When I deploy my web site, I only want to deploy the classes that are going to be used so as not to clutter the server. How then, would I test onSubmit()?
                    Have seperate source trees. My projects generally look like this:

                    Code:
                     project_root
                        src
                          uk.ac.....
                        unit-test
                          uk.ac....
                        war
                          WEB-INF
                            jsps
                            servlet-context.xml
                    See what I mean? Both src and unit-test are "source" trees.
                    Last edited by Colin Yates; Apr 28th, 2006, 07:20 PM.

                    Comment

                    Working...
                    X