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 }