0001: package com.mockrunner.struts;
0002:
0003: import java.io.File;
0004: import java.io.FileInputStream;
0005: import java.util.ArrayList;
0006: import java.util.Iterator;
0007: import java.util.List;
0008: import java.util.Locale;
0009:
0010: import javax.servlet.ServletException;
0011: import javax.sql.DataSource;
0012:
0013: import org.apache.commons.beanutils.BeanUtils;
0014: import org.apache.commons.validator.ValidatorResources;
0015: import org.apache.struts.Globals;
0016: import org.apache.struts.action.Action;
0017: import org.apache.struts.action.ActionErrors;
0018: import org.apache.struts.action.ActionForm;
0019: import org.apache.struts.action.ActionForward;
0020: import org.apache.struts.action.ActionMapping;
0021: import org.apache.struts.action.ActionMessage;
0022: import org.apache.struts.action.ActionMessages;
0023: import org.apache.struts.action.DynaActionForm;
0024: import org.apache.struts.action.DynaActionFormClass;
0025: import org.apache.struts.config.FormBeanConfig;
0026: import org.apache.struts.config.MessageResourcesConfig;
0027: import org.apache.struts.taglib.html.Constants;
0028: import org.apache.struts.util.MessageResources;
0029: import org.apache.struts.validator.ValidatorPlugIn;
0030:
0031: import com.mockrunner.base.HTMLOutputModule;
0032: import com.mockrunner.base.NestedApplicationException;
0033: import com.mockrunner.base.VerifyFailedException;
0034: import com.mockrunner.mock.web.ActionMockObjectFactory;
0035: import com.mockrunner.mock.web.MockActionForward;
0036: import com.mockrunner.mock.web.MockActionMapping;
0037: import com.mockrunner.mock.web.MockPageContext;
0038: import com.mockrunner.util.common.StreamUtil;
0039:
0040: /**
0041: * Module for Struts action tests. Simulates Struts
0042: * without reading the <i>struts-config.xml</i> file.
0043: * Per default this class does everything like Struts
0044: * when calling an action but you can change the behaviour
0045: * (e.g. disable form population).
0046: * Please note: If your action throws an exception and an
0047: * exception handler is registered (use {@link #addExceptionHandler}),
0048: * the handler will be called to handle the exception.
0049: * Otherwise the exception will be rethrown as {@link com.mockrunner.base.NestedApplicationException}.
0050: */
0051: public class ActionTestModule extends HTMLOutputModule {
0052: private ActionMockObjectFactory mockFactory;
0053: private MockActionForward forward;
0054: private ActionForm formObj;
0055: private Action actionObj;
0056: private boolean reset;
0057: private boolean doPopulate;
0058: private boolean recognizeInSession;
0059: private String messageAttributeKey;
0060: private String errorAttributeKey;
0061: private List exceptionHandlers;
0062:
0063: public ActionTestModule(ActionMockObjectFactory mockFactory) {
0064: super (mockFactory);
0065: this .mockFactory = mockFactory;
0066: reset = true;
0067: doPopulate = true;
0068: recognizeInSession = true;
0069: messageAttributeKey = Globals.MESSAGE_KEY;
0070: errorAttributeKey = Globals.ERROR_KEY;
0071: exceptionHandlers = new ArrayList();
0072: }
0073:
0074: /**
0075: * Set if the reset method should be called before
0076: * populating a form with {@link #populateRequestToForm}.
0077: * Default is <code>true</code> which is the standard Struts
0078: * behaviour.
0079: * @param reset should reset be called
0080: */
0081: public void setReset(boolean reset) {
0082: this .reset = reset;
0083: }
0084:
0085: /**
0086: * Set if the form should be populated with the request
0087: * parameters before calling the action.
0088: * Default is <code>true</code> which is the standard Struts
0089: * behaviour.
0090: * @param doPopulate should population be performed
0091: */
0092: public void setDoPopulate(boolean doPopulate) {
0093: this .doPopulate = doPopulate;
0094: }
0095:
0096: /**
0097: * Set if messages that are saved to the session (instead of
0098: * the request) should be recognized.
0099: * Default is <code>true</code>.
0100: * @param recognizeInSession should messages in the session be recognized
0101: */
0102: public void setRecognizeMessagesInSession(boolean recognizeInSession) {
0103: this .recognizeInSession = recognizeInSession;
0104: }
0105:
0106: /**
0107: * Name of the key under which messages are stored. Default is
0108: * <code>Globals.MESSAGE_KEY</code>.
0109: * @param messageAttributeKey the message key
0110: */
0111: public void setMessageAttributeKey(String messageAttributeKey) {
0112: this .messageAttributeKey = messageAttributeKey;
0113: }
0114:
0115: /**
0116: * Name of the key under which errors are stored. Default is
0117: * <code>Globals.ERROR_KEY</code>.
0118: * @param errorAttributeKey the message key
0119: */
0120: public void setErrorAttributeKey(String errorAttributeKey) {
0121: this .errorAttributeKey = errorAttributeKey;
0122: }
0123:
0124: /**
0125: * Convenience method for map backed properties. Creates a String
0126: * <i>value(property)</i>.
0127: * @param property the property
0128: * @return the String in map backed propery style
0129: */
0130: public String addMappedPropertyRequestPrefix(String property) {
0131: return "value(" + property + ")";
0132: }
0133:
0134: /**
0135: * Sets the parameter by calling <code>ActionMapping.setParameter</code>
0136: * on the action mapping returned by {@link #getActionMapping}.
0137: * You can test your Actions with different parameter settings in the
0138: * same test method.
0139: * @param parameter the parameter
0140: */
0141: public void setParameter(String parameter) {
0142: getActionMapping().setParameter(parameter);
0143: }
0144:
0145: /**
0146: * Sets if form validation should be performed before calling the action.
0147: * Calls <code>ActionMapping.setValidate</code> on the action mapping returned
0148: * by {@link #getActionMapping}. Default is <code>false</code>.
0149: * @param validate should validation be performed
0150: */
0151: public void setValidate(boolean validate) {
0152: getActionMapping().setValidate(validate);
0153: }
0154:
0155: /**
0156: * Sets the input attribute. If form validation fails, the
0157: * input attribute can be verified with {@link #verifyForward}.
0158: * Calls <code>ActionMapping.setInput</code> on the action mapping returned
0159: * by {@link #getActionMapping}.
0160: * @param input the input attribute
0161: */
0162: public void setInput(String input) {
0163: getActionMapping().setInput(input);
0164: }
0165:
0166: /**
0167: * Registers an exception handler. The exception handler will
0168: * be called if an action throws an exception. Usually, you
0169: * will pass an instance of {@link DefaultExceptionHandlerConfig}
0170: * to this method. {@link DefaultExceptionHandlerConfig}
0171: * relies on Struts <code>ExceptionHandler</code> classes.
0172: * In special cases, you may add own implementations of
0173: * {@link ExceptionHandlerConfig}, that may be independent from
0174: * the Struts exception handling mechanism.
0175: * If no matching handler is registered, the exception will be rethrown
0176: * as {@link com.mockrunner.base.NestedApplicationException}.
0177: * @param handler the exception handler
0178: */
0179: public void addExceptionHandler(ExceptionHandlerConfig handler) {
0180: if (null != handler) {
0181: exceptionHandlers.add(handler);
0182: }
0183: }
0184:
0185: /**
0186: * Sets the specified messages resources as a request attribute
0187: * using <code>Globals.MESSAGES_KEY</code> as the key. You can
0188: * use this method, if your action calls
0189: * <code>Action.getResources(HttpServletRequest)</code>.
0190: * The deprecated method <code>Action.getResources()</code>
0191: * takes the resources from the servlet context with the same key.
0192: * If your action uses this method, you have to set the resources
0193: * manually to the servlet context.
0194: * @param resources the messages resources
0195: */
0196: public void setResources(MessageResources resources) {
0197: mockFactory.getWrappedRequest().setAttribute(
0198: Globals.MESSAGES_KEY, resources);
0199: }
0200:
0201: /**
0202: * Sets the specified messages resources as a servlet context
0203: * attribute using the specified key and the module config prefix.
0204: * You can use this method, if your action calls
0205: * <code>Action.getResources(HttpServletRequest, String)</code>.
0206: * Please note that the {@link com.mockrunner.mock.web.MockModuleConfig}
0207: * is set by Mockrunner as the current module. It has the name <i>testmodule</i>.
0208: * This can be changed with <code>ModuleConfig.setPrefix</code>.
0209: * @param key the key of the messages resources
0210: * @param resources the messages resources
0211: */
0212: public void setResources(String key, MessageResources resources) {
0213: MessageResourcesConfig config = new MessageResourcesConfig();
0214: config.setKey(key);
0215: mockFactory.getMockModuleConfig().addMessageResourcesConfig(
0216: config);
0217: key = key + mockFactory.getMockModuleConfig().getPrefix();
0218: mockFactory.getMockServletContext()
0219: .setAttribute(key, resources);
0220: }
0221:
0222: /**
0223: * Sets the specified <code>DataSource</code>.
0224: * You can use this method, if your action calls
0225: * <code>Action.getDataSource(HttpServletRequest)</code>.
0226: * @param dataSource <code>DataSource</code>
0227: */
0228: public void setDataSource(DataSource dataSource) {
0229: setDataSource("org.apache.struts.action.DATA_SOURCE",
0230: dataSource);
0231: }
0232:
0233: /**
0234: * Sets the specified <code>DataSource</code>.
0235: * You can use this method, if your action calls
0236: * <code>Action.getDataSource(HttpServletRequest, String)</code>.
0237: * @param key the key of the <code>DataSource</code>
0238: * @param dataSource <code>DataSource</code>
0239: */
0240: public void setDataSource(String key, DataSource dataSource) {
0241: key = key + mockFactory.getMockModuleConfig().getPrefix();
0242: mockFactory.getMockServletContext().setAttribute(key,
0243: dataSource);
0244: }
0245:
0246: /**
0247: * Sets the specified locale as a session attribute
0248: * using <code>Globals.LOCALE_KEY</code> as the key. You can
0249: * use this method, if your action calls
0250: * <code>Action.getLocale(HttpServletRequest)</code>.
0251: * @param locale the locale
0252: */
0253: public void setLocale(Locale locale) {
0254: mockFactory.getMockSession().setAttribute(Globals.LOCALE_KEY,
0255: locale);
0256: }
0257:
0258: /**
0259: * Creates a valid <code>ValidatorResources</code> object based
0260: * on the specified config files. Since the parsing of the files
0261: * is time consuming, it makes sense to cache the result.
0262: * You can set the returned <code>ValidatorResources</code> object
0263: * with {@link #setValidatorResources}. It is then used in
0264: * all validations.
0265: * @param resourcesFiles the array of config files
0266: */
0267: public ValidatorResources createValidatorResources(
0268: String[] resourcesFiles) {
0269: if (resourcesFiles.length == 0)
0270: return null;
0271: setUpServletContextResourcePath(resourcesFiles);
0272: String resourceString = resourcesFiles[0];
0273: for (int ii = 1; ii < resourcesFiles.length; ii++) {
0274: resourceString += "," + resourcesFiles[ii];
0275: }
0276: ValidatorPlugIn plugIn = new ValidatorPlugIn();
0277: plugIn.setPathnames(resourceString);
0278: try {
0279: plugIn.init(mockFactory.getMockActionServlet(), mockFactory
0280: .getMockModuleConfig());
0281: } catch (ServletException exc) {
0282: throw new RuntimeException(
0283: "Error initializing ValidatorPlugIn: "
0284: + exc.getMessage());
0285: }
0286: String key = ValidatorPlugIn.VALIDATOR_KEY
0287: + mockFactory.getMockModuleConfig().getPrefix();
0288: return (ValidatorResources) mockFactory.getMockServletContext()
0289: .getAttribute(key);
0290: }
0291:
0292: private void setUpServletContextResourcePath(String[] resourcesFiles) {
0293: for (int ii = 0; ii < resourcesFiles.length; ii++) {
0294: String file = resourcesFiles[ii];
0295: try {
0296: File streamFile = new File(file);
0297: FileInputStream stream = new FileInputStream(streamFile);
0298: byte[] fileData = StreamUtil
0299: .getStreamAsByteArray(stream);
0300: mockFactory.getMockServletContext()
0301: .setResourceAsStream(file, fileData);
0302: mockFactory.getMockServletContext().setResource(file,
0303: streamFile.toURL());
0304: } catch (Exception exc) {
0305: throw new NestedApplicationException(exc);
0306: }
0307: }
0308: }
0309:
0310: /**
0311: * Sets the specified <code>ValidatorResources</code>. The easiest
0312: * way to create <code>ValidatorResources</code> is the method
0313: * {@link #createValidatorResources}.
0314: * @param validatorResources the <code>ValidatorResources</code>
0315: */
0316: public void setValidatorResources(
0317: ValidatorResources validatorResources) {
0318: String key = ValidatorPlugIn.VALIDATOR_KEY
0319: + mockFactory.getMockModuleConfig().getPrefix();
0320: mockFactory.getMockServletContext().setAttribute(key,
0321: validatorResources);
0322: }
0323:
0324: /**
0325: * Verifies the forward path returned by the action.
0326: * If your action uses <code>mapping.findForward("success")</code>
0327: * to find the forward, you can use this method or
0328: * {@link #verifyForwardName} to test the <code>success</code> forward
0329: * name. If your action creates an <code>ActionForward</code> on its
0330: * own you can use this method to verify the forward <code>path</code>.
0331: * @param path the expected path
0332: * @throws VerifyFailedException if verification fails
0333: */
0334: public void verifyForward(String path) {
0335: if (null == getActionForward()) {
0336: throw new VerifyFailedException("ActionForward == null");
0337: } else if (!getActionForward().verifyPath(path)) {
0338: throw new VerifyFailedException("expected " + path
0339: + ", received " + getActionForward().getPath());
0340: }
0341: }
0342:
0343: /**
0344: * Verifies the forward name returned by the action.
0345: * If your action uses <code>mapping.findForward("success")</code>
0346: * to find the forward, you can use this method or
0347: * {@link #verifyForward} to test the <code>success</code> forward
0348: * name. If your action creates an <code>ActionForward</code> on its
0349: * own you can use this method to verify the forward <code>name</code>.
0350: * @param name the expected name
0351: * @throws VerifyFailedException if verification fails
0352: */
0353: public void verifyForwardName(String name) {
0354: if (null == getActionForward()) {
0355: throw new VerifyFailedException("ActionForward == null");
0356: } else if (!getActionForward().verifyName(name)) {
0357: throw new VerifyFailedException("expected " + name
0358: + ", received " + getActionForward().getName());
0359: }
0360: }
0361:
0362: /**
0363: * Verifies the redirect attribute.
0364: * @param redirect the expected redirect attribute
0365: * @throws VerifyFailedException if verification fails
0366: */
0367: public void verifyRedirect(boolean redirect) {
0368: if (null == getActionForward()) {
0369: throw new VerifyFailedException("ActionForward == null");
0370: } else if (!getActionForward().verifyRedirect(redirect)) {
0371: throw new VerifyFailedException("expected " + redirect
0372: + ", received " + getActionForward().getRedirect());
0373: }
0374: }
0375:
0376: /**
0377: * Verifies that there are no action errors present.
0378: * @throws VerifyFailedException if verification fails
0379: */
0380: public void verifyNoActionErrors() {
0381: verifyNoActionMessages(getActionErrors());
0382: }
0383:
0384: /**
0385: * Verifies that there are no action messages present.
0386: * @throws VerifyFailedException if verification fails
0387: */
0388: public void verifyNoActionMessages() {
0389: verifyNoActionMessages(getActionMessages());
0390: }
0391:
0392: private void verifyNoActionMessages(ActionMessages messages) {
0393: if (containsMessages(messages)) {
0394: StringBuffer buffer = new StringBuffer();
0395: buffer.append("has the following messages/errors: ");
0396: Iterator iterator = messages.get();
0397: while (iterator.hasNext()) {
0398: ActionMessage message = (ActionMessage) iterator.next();
0399: buffer.append(message.getKey() + ";");
0400: }
0401: throw new VerifyFailedException(buffer.toString());
0402: }
0403: }
0404:
0405: /**
0406: * Verifies that there are action errors present.
0407: * @throws VerifyFailedException if verification fails
0408: */
0409: public void verifyHasActionErrors() {
0410: if (!containsMessages(getActionErrors())) {
0411: throw new VerifyFailedException("no action errors");
0412: }
0413: }
0414:
0415: /**
0416: * Verifies that there are action messages present.
0417: * @throws VerifyFailedException if verification fails
0418: */
0419: public void verifyHasActionMessages() {
0420: if (!containsMessages(getActionMessages())) {
0421: throw new VerifyFailedException("no action messages");
0422: }
0423: }
0424:
0425: /**
0426: * Verifies that an action error with the specified key
0427: * is present.
0428: * @param errorKey the expected error key
0429: * @throws VerifyFailedException if verification fails
0430: */
0431: public void verifyActionErrorPresent(String errorKey) {
0432: verifyActionMessagePresent(errorKey, getActionErrors());
0433: }
0434:
0435: /**
0436: * Verifies that an action message with the specified key
0437: * is present.
0438: * @param messageKey the expected message key
0439: * @throws VerifyFailedException if verification fails
0440: */
0441: public void verifyActionMessagePresent(String messageKey) {
0442: verifyActionMessagePresent(messageKey, getActionMessages());
0443: }
0444:
0445: private void verifyActionMessagePresent(String messageKey,
0446: ActionMessages messages) {
0447: if (!containsMessages(messages))
0448: throw new VerifyFailedException("no action messages/errors");
0449: Iterator iterator = messages.get();
0450: while (iterator.hasNext()) {
0451: ActionMessage message = (ActionMessage) iterator.next();
0452: if (message.getKey().equals(messageKey)) {
0453: return;
0454: }
0455: }
0456: throw new VerifyFailedException("message/error " + messageKey
0457: + " not present");
0458: }
0459:
0460: /**
0461: * Verifies that an action error with the specified key
0462: * is not present.
0463: * @param errorKey the error key
0464: * @throws VerifyFailedException if verification fails
0465: */
0466: public void verifyActionErrorNotPresent(String errorKey) {
0467: verifyActionMessageNotPresent(errorKey, getActionErrors());
0468: }
0469:
0470: /**
0471: * Verifies that an action message with the specified key
0472: * is not present.
0473: * @param messageKey the message key
0474: * @throws VerifyFailedException if verification fails
0475: */
0476: public void verifyActionMessageNotPresent(String messageKey) {
0477: verifyActionMessageNotPresent(messageKey, getActionMessages());
0478: }
0479:
0480: private void verifyActionMessageNotPresent(String messageKey,
0481: ActionMessages messages) {
0482: if (!containsMessages(messages))
0483: return;
0484: Iterator iterator = messages.get();
0485: while (iterator.hasNext()) {
0486: ActionMessage message = (ActionMessage) iterator.next();
0487: if (message.getKey().equals(messageKey)) {
0488: throw new VerifyFailedException("message/error "
0489: + messageKey + " present");
0490: }
0491: }
0492: }
0493:
0494: /**
0495: * Verifies that the specified action errors are present.
0496: * Regards number and order of action errors.
0497: * @param errorKeys the array of expected error keys
0498: * @throws VerifyFailedException if verification fails
0499: */
0500: public void verifyActionErrors(String errorKeys[]) {
0501: verifyActionMessages(errorKeys, getActionErrors());
0502: }
0503:
0504: /**
0505: * Verifies that the specified action messages are present.
0506: * Regards number and order of action messages.
0507: * @param messageKeys the array of expected message keys
0508: * @throws VerifyFailedException if verification fails
0509: */
0510: public void verifyActionMessages(String messageKeys[]) {
0511: verifyActionMessages(messageKeys, getActionMessages());
0512: }
0513:
0514: private void verifyActionMessages(String messageKeys[],
0515: ActionMessages messages) {
0516: if (!containsMessages(messages))
0517: throw new VerifyFailedException("no action messages/errors");
0518: if (messages.size() != messageKeys.length)
0519: throw new VerifyFailedException("expected "
0520: + messageKeys.length
0521: + " messages/errors, received " + messages.size()
0522: + " messages/errors");
0523: Iterator iterator = messages.get();
0524: for (int ii = 0; ii < messageKeys.length; ii++) {
0525: ActionMessage message = (ActionMessage) iterator.next();
0526: if (!message.getKey().equals(messageKeys[ii])) {
0527: throw new VerifyFailedException("mismatch at position "
0528: + ii + ", actual: " + message.getKey()
0529: + ", expected: " + messageKeys[ii]);
0530: }
0531: }
0532: }
0533:
0534: /**
0535: * Verifies the values of the action error with the
0536: * specified key. Regards number and order of values.
0537: * @param errorKey the error key
0538: * @param values the exepcted values
0539: * @throws VerifyFailedException if verification fails
0540: */
0541: public void verifyActionErrorValues(String errorKey, Object[] values) {
0542: ActionMessage error = getActionErrorByKey(errorKey);
0543: if (null == error)
0544: throw new VerifyFailedException("action error " + errorKey
0545: + " not present");
0546: verifyActionMessageValues(error, values);
0547: }
0548:
0549: /**
0550: * Verifies the values of the action message with the
0551: * specified key. Regards number and order of values.
0552: * @param messageKey the message key
0553: * @param values the exepcted values
0554: * @throws VerifyFailedException if verification fails
0555: */
0556: public void verifyActionMessageValues(String messageKey,
0557: Object[] values) {
0558: ActionMessage message = getActionMessageByKey(messageKey);
0559: if (null == message)
0560: throw new VerifyFailedException("action message "
0561: + messageKey + " not present");
0562: verifyActionMessageValues(message, values);
0563: }
0564:
0565: private void verifyActionMessageValues(ActionMessage message,
0566: Object[] values) {
0567: Object[] actualValues = message.getValues();
0568: if (null == actualValues)
0569: throw new VerifyFailedException("action message/error "
0570: + message.getKey() + " has no values");
0571: if (values.length != actualValues.length)
0572: throw new VerifyFailedException("action message/error "
0573: + message.getKey() + " has " + actualValues
0574: + " values");
0575: for (int ii = 0; ii < actualValues.length; ii++) {
0576: if (!values[ii].equals(actualValues[ii])) {
0577: throw new VerifyFailedException("action message/error "
0578: + message.getKey() + ": expected value[" + ii
0579: + "]: " + values[ii] + " received value[" + ii
0580: + "]: " + actualValues[ii]);
0581: }
0582: }
0583: }
0584:
0585: /**
0586: * Verifies the value of the action error with the
0587: * specified key. Fails if the specified value does
0588: * not match the actual value or if the error has more
0589: * than one value.
0590: * @param errorKey the error key
0591: * @param value the exepcted value
0592: * @throws VerifyFailedException if verification fails
0593: */
0594: public void verifyActionErrorValue(String errorKey, Object value) {
0595: verifyActionErrorValues(errorKey, new Object[] { value });
0596: }
0597:
0598: /**
0599: * Verifies the value of the action message with the
0600: * specified key. Fails if the specified value does
0601: * not match the actual value or if the message has more
0602: * than one value.
0603: * @param messageKey the message key
0604: * @param value the exepcted value
0605: * @throws VerifyFailedException if verification fails
0606: */
0607: public void verifyActionMessageValue(String messageKey, Object value) {
0608: verifyActionMessageValues(messageKey, new Object[] { value });
0609: }
0610:
0611: /**
0612: * Verifies that the specified error is stored for the specified
0613: * property.
0614: * @param errorKey the error key
0615: * @param property the exepcted value
0616: * @throws VerifyFailedException if verification fails
0617: */
0618: public void verifyActionErrorProperty(String errorKey,
0619: String property) {
0620: verifyActionMessageProperty(errorKey, property,
0621: getActionErrors());
0622: }
0623:
0624: /**
0625: * Verifies that the specified message is stored for the specified
0626: * property.
0627: * @param messageKey the message key
0628: * @param property the exepcted value
0629: * @throws VerifyFailedException if verification fails
0630: */
0631: public void verifyActionMessageProperty(String messageKey,
0632: String property) {
0633: verifyActionMessageProperty(messageKey, property,
0634: getActionMessages());
0635: }
0636:
0637: private void verifyActionMessageProperty(String messageKey,
0638: String property, ActionMessages messages) {
0639: verifyActionMessagePresent(messageKey, messages);
0640: Iterator iterator = messages.get(property);
0641: while (iterator.hasNext()) {
0642: ActionMessage message = (ActionMessage) iterator.next();
0643: if (message.getKey().equals(messageKey))
0644: return;
0645: }
0646: throw new VerifyFailedException("action message/error "
0647: + messageKey + " not present for property " + property);
0648: }
0649:
0650: /**
0651: * Verifies the number of action errors.
0652: * @param number the expected number of errors
0653: * @throws VerifyFailedException if verification fails
0654: */
0655: public void verifyNumberActionErrors(int number) {
0656: verifyNumberActionMessages(number, getActionErrors());
0657: }
0658:
0659: /**
0660: * Verifies the number of action messages.
0661: * @param number the expected number of messages
0662: * @throws VerifyFailedException if verification fails
0663: */
0664: public void verifyNumberActionMessages(int number) {
0665: verifyNumberActionMessages(number, getActionMessages());
0666: }
0667:
0668: private void verifyNumberActionMessages(int number,
0669: ActionMessages messages) {
0670: if (null != messages) {
0671: if (messages.size() == number)
0672: return;
0673: throw new VerifyFailedException("expected " + number
0674: + " messages/errors, received " + messages.size()
0675: + " messages/errors");
0676: }
0677: if (number == 0)
0678: return;
0679: throw new VerifyFailedException("no action messages/errors");
0680: }
0681:
0682: /**
0683: * Returns the action error with the specified key or null
0684: * if such an error does not exist.
0685: * @param errorKey the error key
0686: * @return the action error with the specified key
0687: */
0688: public ActionMessage getActionErrorByKey(String errorKey) {
0689: return getActionMessageByKey(errorKey, getActionErrors());
0690: }
0691:
0692: /**
0693: * Returns the action message with the specified key or null
0694: * if such a message does not exist.
0695: * @param messageKey the message key
0696: * @return the action message with the specified key
0697: */
0698: public ActionMessage getActionMessageByKey(String messageKey) {
0699: return (ActionMessage) getActionMessageByKey(messageKey,
0700: getActionMessages());
0701: }
0702:
0703: private ActionMessage getActionMessageByKey(String messageKey,
0704: ActionMessages messages) {
0705: if (null == messages)
0706: return null;
0707: Iterator iterator = messages.get();
0708: while (iterator.hasNext()) {
0709: ActionMessage message = (ActionMessage) iterator.next();
0710: if (message.getKey().equals(messageKey)) {
0711: return message;
0712: }
0713: }
0714: return null;
0715: }
0716:
0717: /**
0718: * Sets the specified <code>ActionMessages</code> object
0719: * as the currently present messages to the request.
0720: * @param messages the ActionMessages object
0721: */
0722: public void setActionMessages(ActionMessages messages) {
0723: mockFactory.getWrappedRequest().setAttribute(
0724: messageAttributeKey, messages);
0725: }
0726:
0727: /**
0728: * Sets the specified <code>ActionMessages</code> object
0729: * as the currently present messages to the session.
0730: * @param messages the ActionMessages object
0731: */
0732: public void setActionMessagesToSession(ActionMessages messages) {
0733: mockFactory.getMockSession().setAttribute(messageAttributeKey,
0734: messages);
0735: }
0736:
0737: /**
0738: * Get the currently present action messages. Can be called
0739: * after {@link #actionPerform} to get the messages the action
0740: * has set. If messages in the session are recognized
0741: * (use {@link #setRecognizeMessagesInSession}), this method
0742: * returns the union of request and session messages. Otherwise,
0743: * it only returns the request messages.
0744: * @return the action messages
0745: */
0746: public ActionMessages getActionMessages() {
0747: ActionMessages requestMessages = getActionMessagesFromRequest();
0748: ActionMessages sessionMessages = getActionMessagesFromSession();
0749: if (recognizeInSession) {
0750: if (null == requestMessages || requestMessages.isEmpty())
0751: return sessionMessages;
0752: if (null == sessionMessages || sessionMessages.isEmpty())
0753: return requestMessages;
0754: requestMessages = new ActionMessages(requestMessages);
0755: requestMessages.add(sessionMessages);
0756: }
0757: return requestMessages;
0758: }
0759:
0760: /**
0761: * Get the currently present action messages from the request.
0762: * @return the action messages
0763: */
0764: public ActionMessages getActionMessagesFromRequest() {
0765: return (ActionMessages) mockFactory.getWrappedRequest()
0766: .getAttribute(messageAttributeKey);
0767: }
0768:
0769: /**
0770: * Get the currently present action messages from the session.
0771: * @return the action messages
0772: */
0773: public ActionMessages getActionMessagesFromSession() {
0774: return (ActionMessages) mockFactory.getMockSession()
0775: .getAttribute(messageAttributeKey);
0776: }
0777:
0778: /**
0779: * Returns if action messages are present.
0780: * @return true if messages are present, false otherwise
0781: */
0782: public boolean hasActionMessages() {
0783: ActionMessages messages = getActionMessages();
0784: return containsMessages(messages);
0785: }
0786:
0787: /**
0788: * Sets the specified <code>ActionErrors</code> object
0789: * as the currently present errors to the request.
0790: * @param errors the ActionErrors object
0791: */
0792: public void setActionErrors(ActionMessages errors) {
0793: mockFactory.getWrappedRequest().setAttribute(errorAttributeKey,
0794: errors);
0795: }
0796:
0797: /**
0798: * Sets the specified <code>ActionErrors</code> object
0799: * as the currently present errors to the session.
0800: * @param errors the ActionErrors object
0801: */
0802: public void setActionErrorsToSession(ActionMessages errors) {
0803: mockFactory.getMockSession().setAttribute(errorAttributeKey,
0804: errors);
0805: }
0806:
0807: /**
0808: * Get the currently present action errors. Can be called
0809: * after {@link #actionPerform} to get the errors the action
0810: * has set. If messages in the session are recognized
0811: * (use {@link #setRecognizeMessagesInSession}), this method
0812: * returns the union of request and session errors. Otherwise,
0813: * it only returns the request errors.
0814: * @return the action errors
0815: */
0816: public ActionMessages getActionErrors() {
0817: ActionMessages requestErrors = getActionErrorsFromRequest();
0818: ActionMessages sessionErrors = getActionErrorsFromSession();
0819: if (recognizeInSession) {
0820: if (null == requestErrors || requestErrors.isEmpty())
0821: return sessionErrors;
0822: if (null == sessionErrors || sessionErrors.isEmpty())
0823: return requestErrors;
0824: if ((requestErrors instanceof ActionErrors)
0825: || (sessionErrors instanceof ActionErrors)) {
0826: ActionErrors tempErrors = new ActionErrors();
0827: tempErrors.add(requestErrors);
0828: requestErrors = tempErrors;
0829: } else {
0830: requestErrors = new ActionMessages(requestErrors);
0831: }
0832: requestErrors.add(sessionErrors);
0833: }
0834: return requestErrors;
0835: }
0836:
0837: /**
0838: * Get the currently present action errors from the request.
0839: * @return the action messages
0840: */
0841: public ActionMessages getActionErrorsFromRequest() {
0842: return (ActionMessages) mockFactory.getWrappedRequest()
0843: .getAttribute(errorAttributeKey);
0844: }
0845:
0846: /**
0847: * Get the currently present action errors from the session.
0848: * @return the action messages
0849: */
0850: public ActionMessages getActionErrorsFromSession() {
0851: return (ActionMessages) mockFactory.getMockSession()
0852: .getAttribute(errorAttributeKey);
0853: }
0854:
0855: /**
0856: * Returns if action errors are present.
0857: * @return true if errors are present, false otherwise
0858: */
0859: public boolean hasActionErrors() {
0860: ActionMessages errors = getActionErrors();
0861: return containsMessages(errors);
0862: }
0863:
0864: /**
0865: * Delegates to {@link com.mockrunner.mock.web.ActionMockObjectFactory#getMockActionMapping}.
0866: * @return the MockActionMapping
0867: */
0868: public MockActionMapping getMockActionMapping() {
0869: return mockFactory.getMockActionMapping();
0870: }
0871:
0872: /**
0873: * Delegates to {@link com.mockrunner.mock.web.ActionMockObjectFactory#getActionMapping}.
0874: * @return the MockActionMapping
0875: */
0876: public ActionMapping getActionMapping() {
0877: return mockFactory.getActionMapping();
0878: }
0879:
0880: /**
0881: * Returns the <code>MockPageContext</code> object.
0882: * Delegates to {@link com.mockrunner.mock.web.ActionMockObjectFactory#getMockPageContext}.
0883: * @return the MockPageContext
0884: */
0885: public MockPageContext getMockPageContext() {
0886: return mockFactory.getMockPageContext();
0887: }
0888:
0889: /**
0890: * Returns the current <code>ActionForward</code>.
0891: * Can be called after {@link #actionPerform} to get
0892: * the <code>ActionForward</code> the action
0893: * has returned.
0894: * @return the MockActionForward
0895: */
0896: public MockActionForward getActionForward() {
0897: return forward;
0898: }
0899:
0900: /**
0901: * Returns the last tested <code>Action</code> object.
0902: * @return the <code>Action</code> object
0903: */
0904: public Action getLastAction() {
0905: return actionObj;
0906: }
0907:
0908: /**
0909: * Generates a token and sets it to the session and the request.
0910: */
0911: public void generateValidToken() {
0912: String token = String.valueOf(Math.random());
0913: mockFactory.getMockSession().setAttribute(
0914: Globals.TRANSACTION_TOKEN_KEY, token);
0915: addRequestParameter(Constants.TOKEN_KEY, token);
0916: }
0917:
0918: /**
0919: * Returns the current <code>ActionForm</code>.
0920: * @return the <code>ActionForm</code> object
0921: */
0922: public ActionForm getActionForm() {
0923: return formObj;
0924: }
0925:
0926: /**
0927: * Sets the specified <code>ActionForm</code> object as the
0928: * current <code>ActionForm</code>.
0929: * @param formObj the <code>ActionForm</code> object
0930: */
0931: public void setActionForm(ActionForm formObj) {
0932: this .formObj = formObj;
0933: }
0934:
0935: /**
0936: * Creates a new <code>ActionForm</code> object of the specified
0937: * type and sets it as the current <code>ActionForm</code>.
0938: * @param form the <code>Class</code> of the form
0939: */
0940: public ActionForm createActionForm(Class form) {
0941: try {
0942: if (null == form) {
0943: formObj = null;
0944: return null;
0945: }
0946: formObj = (ActionForm) form.newInstance();
0947: return formObj;
0948: } catch (Exception exc) {
0949: throw new NestedApplicationException(exc);
0950: }
0951: }
0952:
0953: /**
0954: * Creates a new <code>DynaActionForm</code> based on the specified
0955: * form config and sets it as the current <code>ActionForm</code>.
0956: * @param formConfig the <code>FormBeanConfig</code>
0957: */
0958: public DynaActionForm createDynaActionForm(FormBeanConfig formConfig) {
0959: try {
0960: if (null == formConfig) {
0961: formObj = null;
0962: return null;
0963: }
0964: DynaActionFormClass formClass = DynaActionFormClass
0965: .createDynaActionFormClass(formConfig);
0966: formObj = (DynaActionForm) formClass.newInstance();
0967: return (DynaActionForm) formObj;
0968: } catch (Exception exc) {
0969: throw new NestedApplicationException(exc);
0970: }
0971: }
0972:
0973: /**
0974: * Populates the current request parameters to the
0975: * <code>ActionForm</code>. The form will be reset
0976: * before populating if reset is enabled ({@link #setReset}.
0977: * If form validation is enabled (use {@link #setValidate}) the
0978: * form will be validated after populating it and the
0979: * appropriate <code>ActionErrors</code> will be set.
0980: */
0981: public void populateRequestToForm() {
0982: try {
0983: handleActionForm();
0984: } catch (Exception exc) {
0985: throw new NestedApplicationException(exc);
0986: }
0987: }
0988:
0989: /**
0990: * Calls the action of the specified type using
0991: * no <code>ActionForm</code>. Sets the current action
0992: * form to <code>null</code>.
0993: * @param action the <code>Class</code> of the action
0994: * @return the resulting <code>ActionForward</code>
0995: */
0996: public ActionForward actionPerform(Class action) {
0997: return actionPerform(action, (ActionForm) null);
0998: }
0999:
1000: /**
1001: * Calls the specified action using
1002: * no <code>ActionForm</code>. Sets the current <code>ActionForm</code>
1003: * to <code>null</code>.
1004: * @param action the <code>Action</code>
1005: * @return the resulting <code>ActionForward</code>
1006: */
1007: public ActionForward actionPerform(Action action) {
1008: return actionPerform(action, (ActionForm) null);
1009: }
1010:
1011: /**
1012: * Calls the action of the specified type using
1013: * the <code>ActionForm</code> of the specified type.
1014: * Creates the appropriate <code>ActionForm</code>, sets it as the
1015: * current <code>ActionForm</code> and populates it before calling the action
1016: * (if populating is disabled, the form will not be populated, use
1017: * {@link #setDoPopulate}).
1018: * If form validation is enabled (use {@link #setValidate}) and
1019: * fails, the action will not be called. In this case,
1020: * the returned <code>ActionForward</code> is based on the
1021: * input attribute. (Set it with {@link #setInput}).
1022: * @param action the <code>Class</code> of the action
1023: * @param form the <code>Class</code> of the form
1024: * @return the resulting <code>ActionForward</code>
1025: */
1026: public ActionForward actionPerform(Class action, Class form) {
1027: createActionForm(form);
1028: return actionPerform(action, formObj);
1029: }
1030:
1031: /**
1032: * Calls the specified action using
1033: * the <code>ActionForm</code> of the specified type.
1034: * Creates the appropriate <code>ActionForm</code>, sets it as the
1035: * current <code>ActionForm</code> and populates it before calling the action
1036: * (if populating is disabled, the form will not be populated, use
1037: * {@link #setDoPopulate}).
1038: * If form validation is enabled (use {@link #setValidate}) and
1039: * fails, the action will not be called. In this case,
1040: * the returned <code>ActionForward</code> is based on the
1041: * input attribute. (Set it with {@link #setInput}).
1042: * @param action the <code>Action</code>
1043: * @param form the <code>Class</code> of the form
1044: * @return the resulting <code>ActionForward</code>
1045: */
1046: public ActionForward actionPerform(Action action, Class form) {
1047: createActionForm(form);
1048: return actionPerform(action, formObj);
1049: }
1050:
1051: /**
1052: * Calls the action of the specified type using
1053: * the specified <code>ActionForm</code> object. The form will
1054: * be set as the current <code>ActionForm</code> and
1055: * will be populated before the action is called (if populating is
1056: * disabled, the form will not be populated, use {@link #setDoPopulate}).
1057: * Please note that request parameters will eventually overwrite
1058: * form values. Furthermore the form will be reset
1059: * before populating it. If you do not want that, disable reset
1060: * using {@link #setReset}. If form validation is enabled
1061: * (use {@link #setValidate}) and fails, the action will not be
1062: * called. In this case, the returned <code>ActionForward</code>
1063: * is based on the input attribute. (Set it with {@link #setInput}).
1064: * @param action the <code>Class</code> of the action
1065: * @param form the <code>ActionForm</code> object
1066: * @return the resulting <code>ActionForward</code>
1067: */
1068: public ActionForward actionPerform(Class action, ActionForm form) {
1069: Action actionToCall = null;
1070: try {
1071: actionToCall = (Action) action.newInstance();
1072: } catch (Exception exc) {
1073: throw new NestedApplicationException(exc);
1074: }
1075: return actionPerform(actionToCall, form);
1076: }
1077:
1078: /**
1079: * Calls the specified action using
1080: * the specified <code>ActionForm</code> object. The form will
1081: * be set as the current <code>ActionForm</code> and
1082: * will be populated before the action is called (if populating is
1083: * disabled, the form will not be populated, use {@link #setDoPopulate}).
1084: * Please note that request parameters will eventually overwrite
1085: * form values. Furthermore the form will be reset
1086: * before populating it. If you do not want that, disable reset
1087: * using {@link #setReset}. If form validation is enabled
1088: * (use {@link #setValidate}) and fails, the action will not be
1089: * called. In this case, the returned <code>ActionForward</code>
1090: * is based on the input attribute. (Set it with {@link #setInput}).
1091: * @param action the <code>Action</code>
1092: * @param form the <code>ActionForm</code> object
1093: * @return the resulting <code>ActionForward</code>
1094: */
1095: public ActionForward actionPerform(Action action, ActionForm form) {
1096: try {
1097: actionObj = action;
1098: actionObj.setServlet(mockFactory.getMockActionServlet());
1099: formObj = form;
1100: setActionErrors(null);
1101: getActionMapping().setType(action.getClass().getName());
1102: if (null != formObj) {
1103: handleActionForm();
1104: }
1105: if (!hasActionErrors()) {
1106: ActionForward currentForward = null;
1107: try {
1108: currentForward = (ActionForward) actionObj.execute(
1109: getActionMapping(), formObj, mockFactory
1110: .getWrappedRequest(), mockFactory
1111: .getWrappedResponse());
1112: } catch (Exception exc) {
1113: ExceptionHandlerConfig handler = findExceptionHandler(exc);
1114: if (null == handler) {
1115: throw exc;
1116: } else {
1117: Object result = handler.handle(exc,
1118: getActionMapping(), formObj,
1119: mockFactory.getWrappedRequest(),
1120: mockFactory.getWrappedResponse());
1121: if (result instanceof ActionForward) {
1122: currentForward = (ActionForward) result;
1123: }
1124: }
1125: }
1126: setResult(currentForward);
1127: } else {
1128: setResult(getActionMapping().getInputForward());
1129: }
1130: } catch (Exception exc) {
1131: throw new NestedApplicationException(exc);
1132: }
1133: return getActionForward();
1134: }
1135:
1136: /**
1137: * Returns the HTML output as a string (if the action creates HTML output).
1138: * Flushes the output before returning it.
1139: * @return the output
1140: */
1141: public String getOutput() {
1142: try {
1143: mockFactory.getMockResponse().getWriter().flush();
1144: } catch (Exception exc) {
1145:
1146: }
1147: return mockFactory.getMockResponse().getOutputStreamContent();
1148: }
1149:
1150: private void setResult(ActionForward currentForward) {
1151: if (null == currentForward) {
1152: forward = null;
1153: } else {
1154: forward = new MockActionForward(currentForward);
1155: }
1156: }
1157:
1158: private ExceptionHandlerConfig findExceptionHandler(Exception exc) {
1159: for (int ii = 0; ii < exceptionHandlers.size(); ii++) {
1160: ExceptionHandlerConfig next = (ExceptionHandlerConfig) exceptionHandlers
1161: .get(ii);
1162: if (next.canHandle(exc))
1163: return next;
1164: }
1165: return null;
1166: }
1167:
1168: private void handleActionForm() throws Exception {
1169: if (reset)
1170: getActionForm().reset(getActionMapping(),
1171: mockFactory.getWrappedRequest());
1172: if (doPopulate)
1173: populateMockRequest();
1174: formObj.setServlet(mockFactory.getMockActionServlet());
1175: if (getActionMapping().getValidate()) {
1176: ActionMessages errors = formObj
1177: .validate(getActionMapping(), mockFactory
1178: .getWrappedRequest());
1179: if (containsMessages(errors)) {
1180: mockFactory.getWrappedRequest().setAttribute(
1181: errorAttributeKey, errors);
1182: }
1183: }
1184: }
1185:
1186: private void populateMockRequest() throws Exception {
1187: BeanUtils.populate(getActionForm(), mockFactory
1188: .getWrappedRequest().getParameterMap());
1189: }
1190:
1191: private boolean containsMessages(ActionMessages messages) {
1192: return (null != messages) && (messages.size() > 0);
1193: }
1194: }
|