001 package com.mockrunner.struts; 002 003 import java.io.FileInputStream; 004 import java.io.FileNotFoundException; 005 import java.util.Iterator; 006 import java.util.Locale; 007 008 import javax.servlet.ServletException; 009 import javax.sql.DataSource; 010 011 import org.apache.commons.beanutils.BeanUtils; 012 import org.apache.commons.logging.Log; 013 import org.apache.commons.logging.LogFactory; 014 import org.apache.commons.validator.ValidatorResources; 015 import org.apache.struts.Globals; 016 import org.apache.struts.action.Action; 017 import org.apache.struts.action.ActionForm; 018 import org.apache.struts.action.ActionForward; 019 import org.apache.struts.action.ActionMessage; 020 import org.apache.struts.action.ActionMessages; 021 import org.apache.struts.action.DynaActionForm; 022 import org.apache.struts.action.DynaActionFormClass; 023 import org.apache.struts.config.FormBeanConfig; 024 import org.apache.struts.config.MessageResourcesConfig; 025 import org.apache.struts.taglib.html.Constants; 026 import org.apache.struts.util.MessageResources; 027 import org.apache.struts.validator.ValidatorPlugIn; 028 029 import com.mockrunner.base.HTMLOutputModule; 030 import com.mockrunner.base.NestedApplicationException; 031 import com.mockrunner.base.VerifyFailedException; 032 import com.mockrunner.mock.web.ActionMockObjectFactory; 033 import com.mockrunner.mock.web.MockActionForward; 034 import com.mockrunner.mock.web.MockActionMapping; 035 import com.mockrunner.mock.web.MockPageContext; 036 import com.mockrunner.util.common.StreamUtil; 037 038 /** 039 * Module for Struts action tests. Simulates Struts 040 * without reading the <i>struts-config.xml</i> file. 041 * Per default this class does everything like Struts 042 * when calling an action but you can change the behaviour 043 * (e.g. disable form population). 044 * Please note: If your action throws an exception, it 045 * will be nested in a {@link com.mockrunner.base.NestedApplicationException}. 046 */ 047 public class ActionTestModule extends HTMLOutputModule 048 { 049 private final static Log log = LogFactory.getLog(ActionTestModule.class); 050 private ActionMockObjectFactory mockFactory; 051 private MockActionForward forward; 052 private ActionForm formObj; 053 private Action actionObj; 054 private boolean reset; 055 private boolean doPopulate; 056 private boolean recognizeInSession; 057 private String messageAttributeKey; 058 private String errorAttributeKey; 059 060 public ActionTestModule(ActionMockObjectFactory mockFactory) 061 { 062 super(mockFactory); 063 this.mockFactory = mockFactory; 064 reset = true; 065 doPopulate = true; 066 recognizeInSession = true; 067 messageAttributeKey = Globals.MESSAGE_KEY; 068 errorAttributeKey = Globals.ERROR_KEY; 069 } 070 071 /** 072 * Set if the reset method should be called before 073 * populating a form with {@link #populateRequestToForm}. 074 * Default is <code>true</code> which is the standard Struts 075 * behaviour. 076 * @param reset should reset be called 077 */ 078 public void setReset(boolean reset) 079 { 080 this.reset = reset; 081 } 082 083 /** 084 * Set if the form should be populated with the request 085 * parameters before calling the action. 086 * Default is <code>true</code> which is the standard Struts 087 * behaviour. 088 * @param doPopulate should population be performed 089 */ 090 public void setDoPopulate(boolean doPopulate) 091 { 092 this.doPopulate = doPopulate; 093 } 094 095 /** 096 * Set if messages that are saved to the session (instead of 097 * the request) should be recognized. 098 * Default is <code>true</code>. 099 * @param recognizeInSession should messages in the session be recognized 100 */ 101 public void setRecognizeMessagesInSession(boolean recognizeInSession) 102 { 103 this.recognizeInSession = recognizeInSession; 104 } 105 106 /** 107 * Name of the key under which messages are stored. Default is 108 * <code>Globals.MESSAGE_KEY</code>. 109 * @param messageAttributeKey the message key 110 */ 111 public void setMessageAttributeKey(String messageAttributeKey) 112 { 113 this.messageAttributeKey = messageAttributeKey; 114 } 115 116 /** 117 * Name of the key under which errors are stored. Default is 118 * <code>Globals.ERROR_KEY</code>. 119 * @param errorAttributeKey the message key 120 */ 121 public void setErrorAttributeKey(String errorAttributeKey) 122 { 123 this.errorAttributeKey = errorAttributeKey; 124 } 125 126 /** 127 * Convinience method for map backed properties. Creates a String 128 * <i>value(property)</i>. 129 * @param property the property 130 * @return the String in map backed propery style 131 */ 132 public String addMappedPropertyRequestPrefix(String property) 133 { 134 return "value(" + property + ")"; 135 } 136 137 /** 138 * Sets the parameter by delegating to {@link MockActionMapping#setParameter}. 139 * You can test your Actions with different parameter settings in the 140 * same test method. 141 * @param parameter the parameter 142 */ 143 public void setParameter(String parameter) 144 { 145 getMockActionMapping().setParameter(parameter); 146 } 147 148 /** 149 * Sets if Form validation should be performed before calling the action. 150 * Delegates to {@link MockActionMapping#setValidate}. Default is false. 151 * @param validate should validation be performed 152 */ 153 public void setValidate(boolean validate) 154 { 155 getMockActionMapping().setValidate(validate); 156 } 157 158 /** 159 * Sets the input attribute. If form validation fails, the 160 * input attribute can be verified with {@link #verifyForward}. 161 * Delegates to {@link MockActionMapping#setInput}. 162 * @param input the input attribute 163 */ 164 public void setInput(String input) 165 { 166 getMockActionMapping().setInput(input); 167 } 168 169 /** 170 * Sets the specified messages resources as a request attribute 171 * using <code>Globals.MESSAGES_KEY</code> as the key. You can 172 * use this method, if your action calls 173 * <code>Action.getResources(HttpServletRequest)</code>. 174 * The deprecated method <code>Action.getResources()</code> 175 * takes the resources from the servlet context with the same key. 176 * If your action uses this method, you have to set the resources 177 * manually to the servlet context. 178 * @param resources the messages resources 179 */ 180 public void setResources(MessageResources resources) 181 { 182 mockFactory.getWrappedRequest().setAttribute(Globals.MESSAGES_KEY, resources); 183 } 184 185 /** 186 * Sets the specified messages resources as a servlet context 187 * attribute using the specified key and the module config prefix. 188 * You can use this method, if your action calls 189 * <code>Action.getResources(HttpServletRequest, String)</code>. 190 * Please note that the {@link com.mockrunner.mock.web.MockModuleConfig} 191 * is set by Mockrunner as the current module. It has the name <i>testmodule</i> 192 * but this can be changed with <code>ModuleConfig.setPrefix</code>. 193 * @param key the key of the messages resources 194 * @param resources the messages resources 195 */ 196 public void setResources(String key, MessageResources resources) 197 { 198 MessageResourcesConfig config = new MessageResourcesConfig(); 199 config.setKey(key); 200 mockFactory.getMockModuleConfig().addMessageResourcesConfig(config); 201 key = key + mockFactory.getMockModuleConfig().getPrefix(); 202 mockFactory.getMockServletContext().setAttribute(key, resources); 203 } 204 205 /** 206 * Sets the specified <code>DataSource</code>. 207 * You can use this method, if your action calls 208 * <code>Action.getDataSource(HttpServletRequest)</code>. 209 * @param dataSource <code>DataSource</code> 210 */ 211 public void setDataSource(DataSource dataSource) 212 { 213 setDataSource(Globals.DATA_SOURCE_KEY, dataSource); 214 } 215 216 /** 217 * Sets the specified <code>DataSource</code>. 218 * You can use this method, if your action calls 219 * <code>Action.getDataSource(HttpServletRequest, String)</code>. 220 * @param key the key of the <code>DataSource</code> 221 * @param dataSource <code>DataSource</code> 222 */ 223 public void setDataSource(String key, DataSource dataSource) 224 { 225 key = key + mockFactory.getMockModuleConfig().getPrefix(); 226 mockFactory.getMockServletContext().setAttribute(key, dataSource); 227 } 228 229 /** 230 * Sets the specified locale as a session attribute 231 * using <code>Globals.LOCALE_KEY</code> as the key. You can 232 * use this method, if your action calls 233 * <code>Action.getLocale(HttpServletRequest)</code>. 234 * @param locale the locale 235 */ 236 public void setLocale(Locale locale) 237 { 238 mockFactory.getMockSession().setAttribute(Globals.LOCALE_KEY, locale); 239 } 240 241 /** 242 * Creates a valid <code>ValidatorResources</code> object based 243 * on the specified config files. Since the parsing of the files 244 * is time consuming, it makes sense to cache the result. 245 * You can set the returned <code>ValidatorResources</code> object 246 * with {@link #setValidatorResources}. It is then used in 247 * all validations. 248 * @param resourcesFiles the array of config files 249 */ 250 public ValidatorResources createValidatorResources(String[] resourcesFiles) 251 { 252 if(resourcesFiles.length == 0) return null; 253 setUpServletContextResourcePath(resourcesFiles); 254 String resourceString = resourcesFiles[0]; 255 for(int ii = 1; ii < resourcesFiles.length; ii++) 256 { 257 resourceString += "," + resourcesFiles[ii]; 258 } 259 ValidatorPlugIn plugIn = new ValidatorPlugIn(); 260 plugIn.setPathnames(resourceString); 261 try 262 { 263 plugIn.init(mockFactory.getMockActionServlet(), mockFactory.getMockModuleConfig()); 264 } 265 catch(ServletException exc) 266 { 267 log.error("Error initializing ValidatorPlugIn", exc); 268 throw new RuntimeException("Error initializing ValidatorPlugIn: " + exc.getMessage()); 269 } 270 String key = ValidatorPlugIn.VALIDATOR_KEY + mockFactory.getMockModuleConfig().getPrefix(); 271 return (ValidatorResources)mockFactory.getMockServletContext().getAttribute(key); 272 } 273 274 private void setUpServletContextResourcePath(String[] resourcesFiles) 275 { 276 for(int ii = 0; ii < resourcesFiles.length; ii++) 277 { 278 String file = resourcesFiles[ii]; 279 try 280 { 281 FileInputStream stream = new FileInputStream(file); 282 byte[] fileData = StreamUtil.getStreamAsByteArray(stream); 283 mockFactory.getMockServletContext().setResourceAsStream(file, fileData); 284 } 285 catch(FileNotFoundException exc) 286 { 287 log.error("File not found", exc); 288 throw new NestedApplicationException(exc); 289 } 290 } 291 } 292 293 /** 294 * Sets the specified <code>ValidatorResources</code>. The easiest 295 * way to create <code>ValidatorResources</code> is the method 296 * {@link #createValidatorResources}. 297 * @param validatorResources the <code>ValidatorResources</code> 298 */ 299 public void setValidatorResources(ValidatorResources validatorResources) 300 { 301 String key = ValidatorPlugIn.VALIDATOR_KEY + mockFactory.getMockModuleConfig().getPrefix(); 302 mockFactory.getMockServletContext().setAttribute(key, validatorResources); 303 } 304 305 /** 306 * Verifies the forward path returned by the action. 307 * If your action uses <code>mapping.findForward("success")</code> 308 * to find the forward, you can use this method or 309 * {@link #verifyForwardName} to test the <code>success</code> forward 310 * name. If your action creates an <code>ActionForward</code> on its 311 * own you can use this method to verify the forward <code>path</code>. 312 * @param path the expected path 313 * @throws VerifyFailedException if verification fails 314 */ 315 public void verifyForward(String path) 316 { 317 if(null == getActionForward()) 318 { 319 throw new VerifyFailedException("ActionForward == null"); 320 } 321 else if (!getActionForward().verifyPath(path)) 322 { 323 throw new VerifyFailedException("expected " + path + ", received " + getActionForward().getPath()); 324 } 325 } 326 327 /** 328 * Verifies the forward name returned by the action. 329 * If your action uses <code>mapping.findForward("success")</code> 330 * to find the forward, you can use this method or 331 * {@link #verifyForward} to test the <code>success</code> forward 332 * name. If your action creates an <code>ActionForward</code> on its 333 * own you can use this method to verify the forward <code>name</code>. 334 * @param name the expected name 335 * @throws VerifyFailedException if verification fails 336 */ 337 public void verifyForwardName(String name) 338 { 339 if(null == getActionForward()) 340 { 341 throw new VerifyFailedException("ActionForward == null"); 342 } 343 else if (!getActionForward().verifyName(name)) 344 { 345 throw new VerifyFailedException("expected " + name + ", received " + getActionForward().getName()); 346 } 347 } 348 349 /** 350 * Verifies the redirect attribute. 351 * @param redirect the expected redirect attribute 352 * @throws VerifyFailedException if verification fails 353 */ 354 public void verifyRedirect(boolean redirect) 355 { 356 if(null == getActionForward()) 357 { 358 throw new VerifyFailedException("ActionForward == null"); 359 } 360 else if(!getActionForward().verifyRedirect(redirect)) 361 { 362 throw new VerifyFailedException("expected " + redirect + ", received " + getActionForward().getRedirect()); 363 } 364 } 365 366 /** 367 * Verifies that there are no action errors present. 368 * @throws VerifyFailedException if verification fails 369 */ 370 public void verifyNoActionErrors() 371 { 372 verifyNoActionMessages(getActionErrors()); 373 } 374 375 /** 376 * Verifies that there are no action messages present. 377 * @throws VerifyFailedException if verification fails 378 */ 379 public void verifyNoActionMessages() 380 { 381 verifyNoActionMessages(getActionMessages()); 382 } 383 384 private void verifyNoActionMessages(ActionMessages messages) 385 { 386 if(containsMessages(messages)) 387 { 388 StringBuffer buffer = new StringBuffer(); 389 buffer.append("has the following messages/errors: "); 390 Iterator iterator = messages.get(); 391 while(iterator.hasNext()) 392 { 393 ActionMessage message = (ActionMessage)iterator.next(); 394 buffer.append(message.getKey() + ";"); 395 } 396 throw new VerifyFailedException(buffer.toString()); 397 } 398 } 399 400 /** 401 * Verifies that there are action errors present. 402 * @throws VerifyFailedException if verification fails 403 */ 404 public void verifyHasActionErrors() 405 { 406 if(!containsMessages(getActionErrors())) 407 { 408 throw new VerifyFailedException("no action errors"); 409 } 410 } 411 412 /** 413 * Verifies that there are action messages present. 414 * @throws VerifyFailedException if verification fails 415 */ 416 public void verifyHasActionMessages() 417 { 418 if(!containsMessages(getActionMessages())) 419 { 420 throw new VerifyFailedException("no action messages"); 421 } 422 } 423 424 /** 425 * Verifies that an action error with the specified key 426 * is present. 427 * @param errorKey the expected error key 428 * @throws VerifyFailedException if verification fails 429 */ 430 public void verifyActionErrorPresent(String errorKey) 431 { 432 verifyActionMessagePresent(errorKey, getActionErrors()); 433 } 434 435 /** 436 * Verifies that an action message with the specified key 437 * is present. 438 * @param messageKey the expected message key 439 * @throws VerifyFailedException if verification fails 440 */ 441 public void verifyActionMessagePresent(String messageKey) 442 { 443 verifyActionMessagePresent(messageKey, getActionMessages()); 444 } 445 446 private void verifyActionMessagePresent(String messageKey, ActionMessages messages) 447 { 448 if(!containsMessages(messages)) throw new VerifyFailedException("no action messages/errors"); 449 Iterator iterator = messages.get(); 450 while (iterator.hasNext()) 451 { 452 ActionMessage message = (ActionMessage) iterator.next(); 453 if(message.getKey().equals(messageKey)) 454 { 455 return; 456 } 457 } 458 throw new VerifyFailedException("message/error " + messageKey + " not present"); 459 } 460 461 /** 462 * Verifies that an action error with the specified key 463 * is not present. 464 * @param errorKey the error key 465 * @throws VerifyFailedException if verification fails 466 */ 467 public void verifyActionErrorNotPresent(String errorKey) 468 { 469 verifyActionMessageNotPresent(errorKey, getActionErrors()); 470 } 471 472 /** 473 * Verifies that an action message with the specified key 474 * is not present. 475 * @param messageKey the message key 476 * @throws VerifyFailedException if verification fails 477 */ 478 public void verifyActionMessageNotPresent(String messageKey) 479 { 480 verifyActionMessageNotPresent(messageKey, getActionMessages()); 481 } 482 483 private void verifyActionMessageNotPresent(String messageKey, ActionMessages messages) 484 { 485 if(!containsMessages(messages)) return; 486 Iterator iterator = messages.get(); 487 while(iterator.hasNext()) 488 { 489 ActionMessage message = (ActionMessage) iterator.next(); 490 if(message.getKey().equals(messageKey)) 491 { 492 throw new VerifyFailedException("message/error " + messageKey + " present"); 493 } 494 } 495 } 496 497 /** 498 * Verifies that the specified action errors are present. 499 * Regards number and order of action errors. 500 * @param errorKeys the array of expected error keys 501 * @throws VerifyFailedException if verification fails 502 */ 503 public void verifyActionErrors(String errorKeys[]) 504 { 505 verifyActionMessages(errorKeys, getActionErrors()); 506 } 507 508 /** 509 * Verifies that the specified action messages are present. 510 * Regards number and order of action messages. 511 * @param messageKeys the array of expected message keys 512 * @throws VerifyFailedException if verification fails 513 */ 514 public void verifyActionMessages(String messageKeys[]) 515 { 516 verifyActionMessages(messageKeys, getActionMessages()); 517 } 518 519 private void verifyActionMessages(String messageKeys[], ActionMessages messages) 520 { 521 if (!containsMessages(messages)) throw new VerifyFailedException("no action messages/errors"); 522 if(messages.size() != messageKeys.length) throw new VerifyFailedException("expected " + messageKeys.length + " messages/errors, received " + messages.size() + " messages/errors"); 523 Iterator iterator = messages.get(); 524 for(int ii = 0; ii < messageKeys.length; ii++) 525 { 526 ActionMessage message = (ActionMessage) iterator.next(); 527 if(!message.getKey().equals(messageKeys[ii])) 528 { 529 throw new VerifyFailedException("mismatch at position " + ii + ", actual: " + message.getKey() + ", expected: " + messageKeys[ii]); 530 } 531 } 532 } 533 534 /** 535 * Verifies the values of the action error with the 536 * specified key. Regards number and order of values. 537 * @param errorKey the error key 538 * @param values the exepcted values 539 * @throws VerifyFailedException if verification fails 540 */ 541 public void verifyActionErrorValues(String errorKey, Object[] values) 542 { 543 ActionMessage error = getActionErrorByKey(errorKey); 544 if(null == error) throw new VerifyFailedException("action error " + errorKey + " not present"); 545 verifyActionMessageValues(error, values); 546 } 547 548 /** 549 * Verifies the values of the action message with the 550 * specified key. Regards number and order of values. 551 * @param messageKey the message key 552 * @param values the exepcted values 553 * @throws VerifyFailedException if verification fails 554 */ 555 public void verifyActionMessageValues(String messageKey, Object[] values) 556 { 557 ActionMessage message = getActionMessageByKey(messageKey); 558 if(null == message) throw new VerifyFailedException("action message " + messageKey + " not present"); 559 verifyActionMessageValues(message, values); 560 } 561 562 private void verifyActionMessageValues(ActionMessage message, Object[] values) 563 { 564 Object[] actualValues = message.getValues(); 565 if(null == actualValues) throw new VerifyFailedException("action message/error " + message.getKey() + " has no values"); 566 if(values.length != actualValues.length) throw new VerifyFailedException("action message/error " + message.getKey() + " has " + actualValues + " values"); 567 for(int ii = 0; ii < actualValues.length; ii++) 568 { 569 if(!values[ii].equals(actualValues[ii])) 570 { 571 throw new VerifyFailedException("action message/error " + message.getKey() + ": expected value[" + ii + "]: " + values[ii] + " received value[" + ii + "]: " + actualValues[ii]); 572 } 573 } 574 } 575 576 /** 577 * Verifies the value of the action error with the 578 * specified key. Fails if the specified value does 579 * not match the actual value or if the error has more 580 * than one value. 581 * @param errorKey the error key 582 * @param value the exepcted value 583 * @throws VerifyFailedException if verification fails 584 */ 585 public void verifyActionErrorValue(String errorKey, Object value) 586 { 587 verifyActionErrorValues(errorKey, new Object[] { value }); 588 } 589 590 /** 591 * Verifies the value of the action message with the 592 * specified key. Fails if the specified value does 593 * not match the actual value or if the message has more 594 * than one value. 595 * @param messageKey the message key 596 * @param value the exepcted value 597 * @throws VerifyFailedException if verification fails 598 */ 599 public void verifyActionMessageValue(String messageKey, Object value) 600 { 601 verifyActionMessageValues(messageKey, new Object[] { value }); 602 } 603 604 /** 605 * Verifies that the specified error is stored for the specified 606 * property. 607 * @param errorKey the error key 608 * @param property the exepcted value 609 * @throws VerifyFailedException if verification fails 610 */ 611 public void verifyActionErrorProperty(String errorKey, String property) 612 { 613 verifyActionMessageProperty(errorKey, property, getActionErrors()); 614 } 615 616 /** 617 * Verifies that the specified message is stored for the specified 618 * property. 619 * @param messageKey the message key 620 * @param property the exepcted value 621 * @throws VerifyFailedException if verification fails 622 */ 623 public void verifyActionMessageProperty(String messageKey, String property) 624 { 625 verifyActionMessageProperty(messageKey, property, getActionMessages()); 626 } 627 628 private void verifyActionMessageProperty(String messageKey, String property, ActionMessages messages) 629 { 630 verifyActionMessagePresent(messageKey, messages); 631 Iterator iterator = messages.get(property); 632 while(iterator.hasNext()) 633 { 634 ActionMessage message = (ActionMessage)iterator.next(); 635 if(message.getKey().equals(messageKey)) return; 636 } 637 throw new VerifyFailedException("action message/error " + messageKey + " not present for property " + property); 638 } 639 640 /** 641 * Verifies the number of action errors. 642 * @param number the expected number of errors 643 * @throws VerifyFailedException if verification fails 644 */ 645 public void verifyNumberActionErrors(int number) 646 { 647 verifyNumberActionMessages(number, getActionErrors()); 648 } 649 650 /** 651 * Verifies the number of action messages. 652 * @param number the expected number of messages 653 * @throws VerifyFailedException if verification fails 654 */ 655 public void verifyNumberActionMessages(int number) 656 { 657 verifyNumberActionMessages(number, getActionMessages()); 658 } 659 660 private void verifyNumberActionMessages(int number, ActionMessages messages) 661 { 662 if (null != messages) 663 { 664 if (messages.size() == number) return; 665 throw new VerifyFailedException("expected " + number + " messages/errors, received " + messages.size() + " messages/errors"); 666 } 667 if (number == 0) return; 668 throw new VerifyFailedException("no action messages/errors"); 669 } 670 671 /** 672 * Returns the action error with the specified key or null 673 * if such an error does not exist. 674 * @param errorKey the error key 675 * @return the action error with the specified key 676 */ 677 public ActionMessage getActionErrorByKey(String errorKey) 678 { 679 return getActionMessageByKey(errorKey, getActionErrors()); 680 } 681 682 /** 683 * Returns the action message with the specified key or null 684 * if such a message does not exist. 685 * @param messageKey the message key 686 * @return the action message with the specified key 687 */ 688 public ActionMessage getActionMessageByKey(String messageKey) 689 { 690 return (ActionMessage)getActionMessageByKey(messageKey, getActionMessages()); 691 } 692 693 private ActionMessage getActionMessageByKey(String messageKey, ActionMessages messages) 694 { 695 if(null == messages) return null; 696 Iterator iterator = messages.get(); 697 while (iterator.hasNext()) 698 { 699 ActionMessage message = (ActionMessage) iterator.next(); 700 if (message.getKey().equals(messageKey)) 701 { 702 return message; 703 } 704 } 705 return null; 706 } 707 708 /** 709 * Sets the specified <code>ActionMessages</code> object 710 * as the currently present messages to the request. 711 * @param messages the ActionMessages object 712 */ 713 public void setActionMessages(ActionMessages messages) 714 { 715 mockFactory.getWrappedRequest().setAttribute(messageAttributeKey, messages); 716 } 717 718 /** 719 * Sets the specified <code>ActionMessages</code> object 720 * as the currently present messages to the session. 721 * @param messages the ActionMessages object 722 */ 723 public void setActionMessagesToSession(ActionMessages messages) 724 { 725 mockFactory.getMockSession().setAttribute(messageAttributeKey, messages); 726 } 727 728 /** 729 * Get the currently present action messages. Can be called 730 * after {@link #actionPerform} to get the messages the action 731 * has set. This method checks the request first. If there are 732 * no messages in the request and messages in the session should 733 * be recognized (use {@link #setRecognizeMessagesInSession}), it checks 734 * the session next. 735 * @return the action messages 736 */ 737 public ActionMessages getActionMessages() 738 { 739 ActionMessages messages = getActionMessagesFromRequest(); 740 if(null == messages && recognizeInSession) 741 { 742 messages = getActionMessagesFromSession(); 743 } 744 return messages; 745 } 746 747 /** 748 * Get the currently present action messages from the request. 749 * @return the action messages 750 */ 751 public ActionMessages getActionMessagesFromRequest() 752 { 753 return (ActionMessages)mockFactory.getWrappedRequest().getAttribute(messageAttributeKey); 754 } 755 756 /** 757 * Get the currently present action messages from the session. 758 * @return the action messages 759 */ 760 public ActionMessages getActionMessagesFromSession() 761 { 762 return (ActionMessages)mockFactory.getMockSession().getAttribute(messageAttributeKey); 763 } 764 765 /** 766 * Returns if action messages are present. 767 * @return true if messages are present, false otherwise 768 */ 769 public boolean hasActionMessages() 770 { 771 ActionMessages messages = getActionMessages(); 772 return containsMessages(messages); 773 } 774 775 /** 776 * Sets the specified <code>ActionErrors</code> object 777 * as the currently present errors to the request. 778 * @param errors the ActionErrors object 779 */ 780 public void setActionErrors(ActionMessages errors) 781 { 782 mockFactory.getWrappedRequest().setAttribute(errorAttributeKey, errors); 783 } 784 785 /** 786 * Sets the specified <code>ActionErrors</code> object 787 * as the currently present errors to the session. 788 * @param errors the ActionErrors object 789 */ 790 public void setActionErrorsToSession(ActionMessages errors) 791 { 792 mockFactory.getMockSession().setAttribute(errorAttributeKey, errors); 793 } 794 795 /** 796 * Get the currently present action errors. Can be called 797 * after {@link #actionPerform} to get the errors the action 798 * has set. This method checks the request first. If there are 799 * no errors in the request and messages in the session should 800 * be recognized (use {@link #setRecognizeMessagesInSession}), it checks 801 * the session next. 802 * @return the action errors 803 */ 804 public ActionMessages getActionErrors() 805 { 806 ActionMessages errors = getActionErrorsFromRequest(); 807 if(null == errors && recognizeInSession) 808 { 809 errors = getActionErrorsFromSession(); 810 } 811 return errors; 812 } 813 814 /** 815 * Get the currently present action errors from the request. 816 * @return the action messages 817 */ 818 public ActionMessages getActionErrorsFromRequest() 819 { 820 return (ActionMessages)mockFactory.getWrappedRequest().getAttribute(errorAttributeKey); 821 } 822 823 /** 824 * Get the currently present action errors from the session. 825 * @return the action messages 826 */ 827 public ActionMessages getActionErrorsFromSession() 828 { 829 return (ActionMessages)mockFactory.getMockSession().getAttribute(errorAttributeKey); 830 } 831 832 /** 833 * Returns if action errors are present. 834 * @return true if errors are present, false otherwise 835 */ 836 public boolean hasActionErrors() 837 { 838 ActionMessages errors = getActionErrors(); 839 return containsMessages(errors); 840 } 841 842 /** 843 * Returns the <code>MockActionMapping</code> passed to 844 * the action. Can be manipulated before and after 845 * {@link #actionPerform}. 846 * Delegates to {@link com.mockrunner.mock.web.ActionMockObjectFactory#getMockActionMapping}. 847 * @return the MockActionMapping 848 */ 849 public MockActionMapping getMockActionMapping() 850 { 851 return mockFactory.getMockActionMapping(); 852 } 853 854 /** 855 * Returns the <code>MockPageContext</code> object. 856 * Delegates to {@link com.mockrunner.mock.web.ActionMockObjectFactory#getMockPageContext}. 857 * @return the MockPageContext 858 */ 859 public MockPageContext getMockPageContext() 860 { 861 return mockFactory.getMockPageContext(); 862 } 863 864 /** 865 * Returns the current <code>ActionForward</code>. 866 * Can be called after {@link #actionPerform} to get 867 * the <code>ActionForward</code> the action 868 * has returned. 869 * @return the MockActionForward 870 */ 871 public MockActionForward getActionForward() 872 { 873 return forward; 874 } 875 876 /** 877 * Returns the last tested <code>Action</code> object. 878 * @return the <code>Action</code> object 879 */ 880 public Action getLastAction() 881 { 882 return actionObj; 883 } 884 885 /** 886 * Generates a token and sets it to the session and the request. 887 */ 888 public void generateValidToken() 889 { 890 String token = String.valueOf(Math.random()); 891 mockFactory.getMockSession().setAttribute(Globals.TRANSACTION_TOKEN_KEY, token); 892 addRequestParameter(Constants.TOKEN_KEY, token); 893 } 894 895 /** 896 * Returns the currently set <code>ActionForm</code>. 897 * @return the <code>ActionForm</code> object 898 */ 899 public ActionForm getActionForm() 900 { 901 return formObj; 902 } 903 904 /** 905 * Sets the specified <code>ActionForm</code> object as the 906 * current <code>ActionForm</code>. Will be used in next test. 907 * @param formObj the <code>ActionForm</code> object 908 */ 909 public void setActionForm(ActionForm formObj) 910 { 911 this.formObj = formObj; 912 } 913 914 /** 915 * Creates a new <code>ActionForm</code> object of the specified 916 * type and sets it as the current <code>ActionForm</code>. 917 * @param form the <code>Class</code> of the form 918 */ 919 public ActionForm createActionForm(Class form) 920 { 921 try 922 { 923 if (null == form) 924 { 925 formObj = null; 926 return null; 927 } 928 formObj = (ActionForm)form.newInstance(); 929 return formObj; 930 } 931 catch(Exception exc) 932 { 933 log.error(exc.getMessage(), exc); 934 throw new NestedApplicationException(exc); 935 } 936 } 937 938 /** 939 * Creates a new <code>DynaActionForm</code> based on the specified 940 * form config. 941 * @param formConfig the <code>FormBeanConfig</code> 942 */ 943 public DynaActionForm createDynaActionForm(FormBeanConfig formConfig) 944 { 945 try 946 { 947 if (null == formConfig) 948 { 949 formObj = null; 950 return null; 951 } 952 DynaActionFormClass formClass = DynaActionFormClass.createDynaActionFormClass(formConfig); 953 formObj = (DynaActionForm)formClass.newInstance(); 954 return (DynaActionForm)formObj; 955 } 956 catch(Exception exc) 957 { 958 log.error(exc.getMessage(), exc); 959 throw new NestedApplicationException(exc); 960 } 961 } 962 963 /** 964 * Populates the current request parameters to the 965 * <code>ActionForm</code>. The form will be reseted 966 * before populating if reset is enabled ({@link #setReset}. 967 * If form validation is enabled (use {@link #setValidate}) the 968 * form will be validated after populating it and the 969 * appropriate <code>ActionErrors</code> will be set. 970 */ 971 public void populateRequestToForm() 972 { 973 try 974 { 975 handleActionForm(); 976 } 977 catch(Exception exc) 978 { 979 log.error(exc.getMessage(), exc); 980 throw new NestedApplicationException(exc); 981 } 982 } 983 984 /** 985 * Calls the action of the specified type using 986 * no <code>ActionForm</code>. 987 * @param action the <code>Class</code> of the action 988 * @return the resulting <code>ActionForward</code> 989 */ 990 public ActionForward actionPerform(Class action) 991 { 992 try 993 { 994 return actionPerform(action, (ActionForm) null); 995 } 996 catch(Exception exc) 997 { 998 throw new NestedApplicationException(exc); 999 } 1000 } 1001 1002 /** 1003 * Calls the specified action using 1004 * no <code>ActionForm</code>. 1005 * @param action the <code>Action</code> 1006 * @return the resulting <code>ActionForward</code> 1007 */ 1008 public ActionForward actionPerform(Action action) 1009 { 1010 try 1011 { 1012 return actionPerform(action, (ActionForm) null); 1013 } 1014 catch(Exception exc) 1015 { 1016 throw new NestedApplicationException(exc); 1017 } 1018 } 1019 1020 /** 1021 * Calls the action of the specified type using 1022 * the <code>ActionForm</code> of the specified type. 1023 * Creates the appropriate <code>ActionForm</code> and 1024 * populates it before calling the action (if populating is 1025 * disabled, the form will not be populated, use {@link #setDoPopulate}). 1026 * If form validation is enabled (use {@link #setValidate}) and 1027 * fails, the action will not be called. In this case, 1028 * the returned <code>ActionForward</code> is based on the 1029 * input attribute. (Set it with {@link #setInput}). 1030 * @param action the <code>Class</code> of the action 1031 * @param form the <code>Class</code> of the form 1032 * @return the resulting <code>ActionForward</code> 1033 */ 1034 public ActionForward actionPerform(Class action, Class form) 1035 { 1036 try 1037 { 1038 createActionForm(form); 1039 return actionPerform(action, formObj); 1040 } 1041 catch(Exception exc) 1042 { 1043 throw new NestedApplicationException(exc); 1044 } 1045 } 1046 1047 /** 1048 * Calls the specified action using 1049 * the <code>ActionForm</code> of the specified type. 1050 * Creates the appropriate <code>ActionForm</code> and 1051 * populates it before calling the action (if populating is 1052 * disabled, the form will not be populated, use {@link #setDoPopulate}). 1053 * If form validation is enabled (use {@link #setValidate}) and 1054 * fails, the action will not be called. In this case, 1055 * the returned <code>ActionForward</code> is based on the 1056 * input attribute. (Set it with {@link #setInput}). 1057 * @param action the <code>Action</code> 1058 * @param form the <code>Class</code> of the form 1059 * @return the resulting <code>ActionForward</code> 1060 */ 1061 public ActionForward actionPerform(Action action, Class form) 1062 { 1063 try 1064 { 1065 createActionForm(form); 1066 return actionPerform(action, formObj); 1067 } 1068 catch(Exception exc) 1069 { 1070 throw new NestedApplicationException(exc); 1071 } 1072 } 1073 1074 /** 1075 * Calls the action of the specified type using 1076 * the specified <code>ActionForm</code> object. The form 1077 * will be populated before the action is called (if populating is 1078 * disabled, the form will not be populated, use {@link #setDoPopulate}). 1079 * Please note that request parameters will eventually overwrite 1080 * form values. Furthermore the form will be reseted 1081 * before populating it. If you do not want that, disable reset 1082 * using {@link #setReset}. If form validation is enabled 1083 * (use {@link #setValidate}) and fails, the action will not be 1084 * called. In this case, the returned <code>ActionForward</code> 1085 * is based on the input attribute. (Set it with {@link #setInput}). 1086 * @param action the <code>Class</code> of the action 1087 * @param form the <code>ActionForm</code> object 1088 * @return the resulting <code>ActionForward</code> 1089 */ 1090 public ActionForward actionPerform(Class action, ActionForm form) 1091 { 1092 try 1093 { 1094 return actionPerform((Action)action.newInstance(), form); 1095 } 1096 catch(Exception exc) 1097 { 1098 throw new NestedApplicationException(exc); 1099 } 1100 } 1101 1102 /** 1103 * Calls the specified action using 1104 * the specified <code>ActionForm</code> object. The form 1105 * will be populated before the action is called (if populating is 1106 * disabled, the form will not be populated, use {@link #setDoPopulate}). 1107 * Please note that request parameters will eventually overwrite 1108 * form values. Furthermore the form will be reseted 1109 * before populating it. If you do not want that, disable reset 1110 * using {@link #setReset}. If form validation is enabled 1111 * (use {@link #setValidate}) and fails, the action will not be 1112 * called. In this case, the returned <code>ActionForward</code> 1113 * is based on the input attribute. (Set it with {@link #setInput}). 1114 * @param action the <code>Action</code> 1115 * @param form the <code>ActionForm</code> object 1116 * @return the resulting <code>ActionForward</code> 1117 */ 1118 public ActionForward actionPerform(Action action, ActionForm form) 1119 { 1120 try 1121 { 1122 actionObj = action; 1123 actionObj.setServlet(mockFactory.getMockActionServlet()); 1124 formObj = form; 1125 setActionErrors(null); 1126 getMockActionMapping().setType(action.getClass().getName()); 1127 if(null != formObj) 1128 { 1129 handleActionForm(); 1130 } 1131 if(!hasActionErrors()) 1132 { 1133 ActionForward currentForward = (ActionForward) actionObj.execute(getMockActionMapping(), formObj, mockFactory.getWrappedRequest(), mockFactory.getWrappedResponse()); 1134 setResult(currentForward); 1135 } 1136 else 1137 { 1138 setResult(getMockActionMapping().getInputForward()); 1139 } 1140 } 1141 catch(Exception exc) 1142 { 1143 throw new NestedApplicationException(exc); 1144 } 1145 return getActionForward(); 1146 } 1147 1148 /** 1149 * Returns the HTML output as a string (if the action creates HTML output). 1150 * Flushes the output before returning it. 1151 * @return the output 1152 */ 1153 public String getOutput() 1154 { 1155 try 1156 { 1157 mockFactory.getMockResponse().getWriter().flush(); 1158 } 1159 catch(Exception exc) 1160 { 1161 log.error(exc.getMessage(), exc); 1162 } 1163 return mockFactory.getMockResponse().getOutputStreamContent(); 1164 } 1165 1166 private void setResult(ActionForward currentForward) 1167 { 1168 if (null == currentForward) 1169 { 1170 forward = null; 1171 } 1172 else 1173 { 1174 forward = new MockActionForward(currentForward); 1175 } 1176 } 1177 1178 private void handleActionForm() throws Exception 1179 { 1180 if(reset) getActionForm().reset(getMockActionMapping(), mockFactory.getWrappedRequest()); 1181 if(doPopulate) populateMockRequest(); 1182 formObj.setServlet(mockFactory.getMockActionServlet()); 1183 if(getMockActionMapping().getValidate()) 1184 { 1185 ActionMessages errors = formObj.validate(getMockActionMapping(), mockFactory.getWrappedRequest()); 1186 if (containsMessages(errors)) 1187 { 1188 mockFactory.getWrappedRequest().setAttribute(errorAttributeKey, errors); 1189 } 1190 } 1191 } 1192 1193 private void populateMockRequest() throws Exception 1194 { 1195 BeanUtils.populate(getActionForm(), mockFactory.getWrappedRequest().getParameterMap()); 1196 } 1197 1198 private boolean containsMessages(ActionMessages messages) 1199 { 1200 return (null != messages) && (messages.size() > 0); 1201 } 1202 }