001: // Copyright © 2002-2007 Canoo Engineering AG, Switzerland.
002: package com.canoo.webtest.steps;
003:
004: import java.io.IOException;
005: import java.lang.reflect.Method;
006: import java.net.URL;
007:
008: import junit.framework.AssertionFailedError;
009:
010: import org.apache.commons.lang.ArrayUtils;
011: import org.apache.commons.lang.StringUtils;
012: import org.apache.tools.ant.Project;
013: import org.apache.tools.ant.Target;
014: import org.apache.tools.ant.Task;
015:
016: import com.agical.rmock.extension.junit.RMockTestCase;
017: import com.canoo.webtest.ant.WebtestPropertyHelper;
018: import com.canoo.webtest.ant.WebtestTask;
019: import com.canoo.webtest.boundary.HtmlUnitBoundary;
020: import com.canoo.webtest.boundary.UrlBoundary;
021: import com.canoo.webtest.engine.StepExecutionException;
022: import com.canoo.webtest.engine.StepFailedException;
023: import com.canoo.webtest.self.ContextStub;
024: import com.canoo.webtest.self.TestBlock;
025: import com.canoo.webtest.self.ThrowAssert;
026: import com.gargoylesoftware.htmlunit.Page;
027: import com.gargoylesoftware.htmlunit.html.HtmlPage;
028:
029: /**
030: * Tests that are common for all Steps, especially parameter handling.
031: *
032: * @author Marc Guillemot
033: * @author Denis N. Antonioli
034: * @author Paul King
035: */
036: public abstract class BaseStepTestCase extends RMockTestCase {
037: protected static final String NO_CURRENT_RESPONSE = "No current response available! Is previous invoke missing?";
038: private static final String NO_CURRENT_RESPONSEFILE = "No current response file available!";
039:
040: private ContextStub fContext;
041: private Step fStep;
042: private Project fProject;
043: private Target fTarget;
044:
045: /**
046: * The minimal string to have the verification of toString pass.
047: * Useful for simplistic implmentation of abstract class.
048: */
049: public static final String MOCK_TO_STRING = ")";
050:
051: /**
052: * Creates the context and the Step under test calling {@link #createStep()}.
053: *
054: * @see junit.framework.TestCase#setUp()
055: */
056: protected void setUp() throws Exception {
057: super .setUp();
058: fProject = new Project();
059: WebtestPropertyHelper.configureWebtestPropertyHelper(fProject);
060: fContext = createContext();
061: fTarget = new Target();
062: fTarget.setProject(fProject);
063: fStep = createAndConfigureStep();
064: }
065:
066: protected void tearDown() throws Exception {
067: super .tearDown();
068: // Junit releases test instances only at the end, we need to release resources earlier
069: fProject = null;
070: fContext = null;
071: fTarget = null;
072: fStep = null;
073: }
074:
075: protected ContextStub getContext() {
076: return fContext;
077: }
078:
079: /**
080: * Creates the context that will be created in {@link #setUp()}, available through {@link #getContext()}
081: * and set on the step created with {@link #createStep()}
082: *
083: * @return the context
084: */
085: protected ContextStub createContext() {
086: final ContextStub cx = new ContextStub();
087: cx.getWebtest().setProject(getProject());
088: return cx;
089: }
090:
091: protected void setFakedContext(final ContextStub context)
092: throws IOException {
093: if (fContext.getCurrentResponse() != null)
094: fContext.getCurrentResponse().cleanUp();
095: fContext = context;
096: WebtestTask.setThreadContext(context);
097: }
098:
099: /**
100: * Gets a html page with a very basic content
101: *
102: * @return the page
103: */
104: protected HtmlPage getDummyPage() {
105: return getDummyPage("<html></html>");
106: }
107:
108: /**
109: * Gets a html page with the given content
110: */
111: protected HtmlPage getDummyPage(final String content) {
112: return (HtmlPage) getDummyPage(content, "text/html");
113: }
114:
115: /**
116: * Gets a page with the given content and content type
117: */
118: protected Page getDummyPage(final String content,
119: final String contentType) {
120: fContext.setDefaultResponse(content, contentType);
121: final URL url = UrlBoundary.tryCreateUrl("http://www.toto.to");
122: return HtmlUnitBoundary.tryGetPage(url, getContext()
123: .getWebClient());
124: }
125:
126: public void testUnknownPropertyType() {
127: final Step step = getStep();
128: try {
129: step.getWebtestProperty("antProp", "unknown");
130: ///CLOVER:OFF will not occur if we write steps correctly
131: fail("Should raise a StepExecutionException");
132: ///CLOVER:ON
133: } catch (StepExecutionException expected) {
134: assertTrue(true);
135: }
136: }
137:
138: public void testToString() {
139: final Step step = getStep();
140:
141: final String result = step.toString();
142: assertNotNull(result);
143: assertTrue("toString() should end with ')' but found: "
144: + result, result.endsWith(")"));
145: }
146:
147: /**
148: * Gets the step beeing tested. This should not be called during class initialisation because
149: * a new instance is created before each test execution.
150: *
151: * @return the currently tested step created during {@link #setUp()} by {@link #createStep()}.
152: */
153: protected final Step getStep() {
154: return fStep;
155: }
156:
157: /**
158: * Concrete test classes should return the Step they want to test.
159: * The step is created during {@link #setUp()} and the context is set on it to {@link #getContext()}
160: *
161: * @return the tested step.
162: */
163: protected abstract Step createStep();
164:
165: /**
166: * Creates a new step using {@link #createStep()} and sets the current context on it
167: *
168: * @return the step
169: */
170: protected final Step createAndConfigureStep() {
171: return configureStep(createStep());
172: }
173:
174: /**
175: * Configures the step a la webtest, setting the properties ant/webtest would have set on it before executing
176: * it (Project, Context, ...).
177: *
178: * @param step the step to configure
179: * @return the configured step
180: */
181: protected Step configureStep(final Step step) {
182: configureTask(step);
183: return step;
184: }
185:
186: /**
187: * Configures the task a la ant, setting the properties ant would have set on it before executing
188: * it (Project, Target, ...).
189: *
190: * @param task the task to configure
191: * @return the configured task
192: */
193: protected Task configureTask(final Task task) {
194: task.setProject(fProject);
195: task.setOwningTarget(fTarget);
196: return task;
197: }
198:
199: /**
200: * Gets the project used for the current test
201: * @return the project
202: */
203: protected Project getProject() {
204: return fProject;
205: }
206:
207: protected void assertStepRejectsNullResponse(final Step step) {
208: getContext().fakeLastResponse(null);
209: assertErrorOnExecute(step, "currentResponse == null",
210: NO_CURRENT_RESPONSE);
211: }
212:
213: protected void assertStepRejectsNullResponseFile(final Step step)
214: throws Exception {
215: getContext().fakeLastResponse(null);
216: assertErrorOnExecute(step, "currentResponseFile == null",
217: NO_CURRENT_RESPONSEFILE);
218: }
219:
220: protected static void checkResponseMessage(
221: final String expectedMessage, final String message) {
222: ///CLOVER:OFF will not occur if we write steps correctly
223: final String actualMessage = null == message ? "null" : message;
224: if (!actualMessage.startsWith(expectedMessage)) {
225: fail("expected start <" + expectedMessage + "> but was <"
226: + actualMessage + ">");
227: }
228: ///CLOVER:ON
229: }
230:
231: protected static void assertStepRejectsNullParam(
232: final String param, final TestBlock b) {
233: final Throwable t = ThrowAssert.assertThrows("param == null",
234: StepExecutionException.class, b);
235: assertEquals("Required parameter \"" + param + "\" not set!", t
236: .getMessage());
237: }
238:
239: protected static void assertStepRejectsEmptyParam(
240: final String param, final TestBlock b) {
241: final Throwable t = ThrowAssert.assertThrows(
242: "param == null or zero length",
243: StepExecutionException.class, b);
244: assertEquals("Required parameter \"" + param
245: + "\" not set or set to empty string!", t.getMessage());
246: }
247:
248: /**
249: * Gets a test block calling {@link #executeStep(Step)} with the provided step.
250: * @return the test block
251: */
252: protected static TestBlock getExecuteStepTestBlock(final Step step) {
253: return new TestBlock() {
254: public void call() throws Exception {
255: executeStep(step);
256: }
257: };
258: }
259:
260: /**
261: * Gets a test block calling {@link #executeStep(Step)} on {@link #getStep()}.
262: * @return the test block
263: */
264: protected TestBlock getExecuteStepTestBlock() {
265: return getExecuteStepTestBlock(getStep());
266: }
267:
268: /**
269: * @return the thrown exception
270: */
271: protected Throwable assertThrowOnExecute(final Step step,
272: final String failMessage,
273: final String exceptionMessagePrefix, final Class throwable) {
274: final Throwable t = ThrowAssert.assertThrows(failMessage,
275: throwable, getExecuteStepTestBlock(step));
276: final String message = t.getMessage();
277: if (!message.startsWith(exceptionMessagePrefix)) {
278: fail("expected start <" + exceptionMessagePrefix
279: + "> but was <" + message + ">");
280: }
281: return t;
282: }
283:
284: /**
285: * @param failMessage
286: * @param exceptionMessagePrefix
287: * @return the thrown exception
288: */
289: protected Throwable assertFailOnExecute(final Step step,
290: final String failMessage,
291: final String exceptionMessagePrefix) {
292: return assertThrowOnExecute(step, failMessage,
293: exceptionMessagePrefix, StepFailedException.class);
294: }
295:
296: /**
297: * @return the thrown exception
298: */
299: protected Throwable assertFailOnExecute(final Step step) {
300: return assertThrowOnExecute(step, "", "",
301: StepFailedException.class);
302: }
303:
304: /**
305: * @return the thrown exception
306: */
307: protected Throwable assertErrorOnExecute(final Step step,
308: final String failMessage,
309: final String exceptionMessagePrefix) {
310: return assertThrowOnExecute(step, failMessage,
311: exceptionMessagePrefix, StepExecutionException.class);
312: }
313:
314: /**
315: * @return the thrown exception
316: */
317: protected Throwable assertErrorOnExecute(final Step step) {
318: return assertThrowOnExecute(step, "", "",
319: StepExecutionException.class);
320: }
321:
322: protected void assertErrorOnExecuteIfCurrentPageIsXml(
323: final Step step) {
324: step.getContext().saveResponseAsCurrent(
325: getDummyPage("<xml></xml>", "text/xml"));
326: ThrowAssert.assertThrows(StepExecutionException.class,
327: "Current response is not an HTML page",
328: new TestBlock() {
329: public void call() throws Exception {
330: executeStep(step);
331: }
332: });
333: }
334:
335: public static void assertInstanceOf(final Class expected,
336: final Object actual) {
337: if (!expected.isInstance(actual)) {
338: fail("expected: <" + expected.getName() + "> but was <"
339: + actual.getClass().getName() + ">.");
340: }
341: }
342:
343: public void testAssertInstanceOf() {
344: assertInstanceOf(Boolean.class, Boolean.TRUE);
345: ThrowAssert.assertThrows(AssertionFailedError.class,
346: new TestBlock() {
347: public void call() throws Throwable {
348: assertInstanceOf(Integer.class, Boolean.TRUE);
349: }
350: });
351: }
352:
353: /**
354: * Use this method to execute a step.
355: * The method duplicates the content of {@link Step#execute()}, but without error
356: * handling and without notification. It assumes the context is already set.
357: *
358: * @param step The step to execute.
359: * @throws Exception
360: */
361: public static void executeStep(final Step step) throws Exception {
362: step.verifyParameters();
363: step.doExecute();
364: }
365:
366: public static ContextStub getContextForDocument(
367: final String documentText) {
368: return new ContextStub(documentText);
369: }
370:
371: /**
372: * Test that calling addText(String) on the step sets the specified property.
373: * @param step the step
374: * @param propertyName the property that addText is expected to fill
375: * @throws Exception
376: */
377: protected void testNestedTextEquivalent(final Step step,
378: final String propertyName) throws Exception {
379: final String textToSet = "blabla";
380: final Method addText = step.getClass().getMethod("addText",
381: new Class[] { String.class });
382: addText.invoke(step, new Object[] { textToSet });
383:
384: final Method getter = step.getClass().getMethod(
385: "get" + StringUtils.capitalize(propertyName),
386: ArrayUtils.EMPTY_CLASS_ARRAY);
387: final String value = (String) getter.invoke(step, null);
388:
389: assertEquals("test value of property " + propertyName,
390: textToSet, value);
391: }
392: }
|