001: /* DDSteps - Data Driven JUnit Test Steps
002: * Copyright (C) 2006 Jayway AB
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License version 2.1 as published by the Free Software Foundation.
007: *
008: * This library is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, visit
015: * http://www.opensource.org/licenses/lgpl-license.php
016: */
017: package org.ddsteps;
018:
019: import junit.framework.TestCase;
020: import junit.framework.TestResult;
021:
022: import org.apache.commons.lang.StringUtils;
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.ddsteps.data.DataLoader;
026: import org.ddsteps.junit.behaviour.DdBehaviour;
027: import org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler;
028: import org.ddsteps.junit.behaviour.StaticBehaviourFactory;
029: import org.ddsteps.util.TestCaseInfo;
030: import org.ddsteps.util.TestCaseNameUtils;
031:
032: /**
033: * DDSteps base class for unit tests that want to controll their own dataloader.
034: * <p>
035: * In your test subclass you must implement:
036: * <ul>
037: * <li>{@link #createDataLoader()}
038: * </ul>
039: * <p>
040: * You may implement these hooks if you want to:
041: * <ul>
042: * <li>{@link #setUpBeforeData()} - called before data is injected. Useful to
043: * create beans that data will be injected into.
044: * <li>{@link #setUpAfterData()} - called after data has been injected. Useful
045: * to do setup based on data that was injected.
046: * <li>{@link #tearDownBeforeData()} - called after your test, the injected
047: * data is still present. Useful to tear down data dependent stuff and to null
048: * out references to heavy objects.
049: * </ul>
050: *
051: * @author adamskogman
052: */
053: public abstract class DDStepsTestCase extends TestCase implements
054: DdBehaviourCallbackHandler {
055:
056: /**
057: * Logger
058: */
059: static Log LOG = LogFactory.getLog(DDStepsTestCase.class);
060:
061: /**
062: * Property: Full, datadriven name of this testcase, i.e. testMethod if a
063: * method instance, or testMethod[Row 13] if a row instance.
064: * <p>
065: * The fName in TestCase must not have this string, instead, it will only
066: * ever have the testMethod, as that variable is used in runTest() to
067: * determine what method to run using reflection.
068: * <p>
069: * This property is set using (@link #setName(String)) which will set fName
070: * in TestCase to only the method part.
071: */
072: String fullName;
073:
074: /**
075: * Dependency: The Behaviour delegate.
076: */
077: DdBehaviour behaviour;
078:
079: /**
080: * Standard, no args constructor.
081: * <p>
082: * The JUnit contract dictates that if no constructor with a String (name)
083: * argument i sfound, this no-args constructor is used, and then setName()
084: * is called. That is how TestSuite works, anyway, and that's the way we
085: * like it. Our (@link #setName(String)) can then get the behaviour, if
086: * needed.
087: */
088: public DDStepsTestCase() {
089: // Call the NO ARGS super constructor, and let setName() handle
090: // getting the right behaviour for us
091: super ();
092: }
093:
094: // ///////////////////////////////////////////////////////////
095: // Overrides that delegate to DdBehaviour
096: // ///////////////////////////////////////////////////////////
097:
098: /**
099: * Delegate to behaviour.
100: *
101: * @see junit.framework.TestCase#countTestCases()
102: */
103: final public int countTestCases() {
104: return getBehaviour().countTestCases();
105: }
106:
107: /**
108: * Delegate to behaviour.
109: *
110: * @see junit.framework.TestCase#run(junit.framework.TestResult)
111: */
112: final public void run(TestResult result) {
113: if (getBehaviour().run(result, this )) {
114: super .run(result);
115: }
116: }
117:
118: /**
119: * Delegate to behaviour.
120: *
121: * @see junit.framework.TestCase#setUp()
122: */
123: final public void setUp() throws Exception {
124: getBehaviour().setUp(this );
125: }
126:
127: /**
128: * Delegate to behaviour.
129: *
130: * @see junit.framework.TestCase#tearDown()
131: */
132: final public void tearDown() throws Exception {
133: getBehaviour().tearDown(this );
134: }
135:
136: // ///////////////////////////////////////////////////////////
137: // Name and behaviour handling
138: // ///////////////////////////////////////////////////////////
139:
140: /**
141: * Overrides setName to handle DDSteps "data driven" names, such as
142: * testMethod[Row 13].
143: * <p>
144: * Overriding this method is the only way we can make sure that the name is
145: * always handled correctly.
146: * <p>
147: * The name can be either that of the test method (i.e. testFoo), or an
148: * indexed name (i.e. testFoo[Row X]. The contents of the bracket is up to
149: * the DataLoader to interpret, but it indicates that this is a row
150: * instance, not the testMethod instance.
151: *
152: * @see junit.framework.TestCase#setName(java.lang.String)
153: */
154: public final void setName(String name) {
155:
156: // Don't do anything if the names are equal
157: if (!StringUtils.equals(name, fullName)) {
158: TestCaseInfo info = TestCaseNameUtils
159: .parseTestCaseName(name);
160:
161: // fName gets the method
162: super .setName(info.getMethodName());
163:
164: // Our field gets full name
165: fullName = name;
166:
167: // Clear the behavior
168: this .behaviour = null;
169: }
170:
171: }
172:
173: /**
174: * Returns the full, data driven name, such as testMethod[Rox 13].
175: * <p>
176: * Some tools, especially eclipse, will use this to display and to recreate
177: * the testcase.
178: *
179: * @see junit.framework.TestCase#getName()
180: */
181: public final String getName() {
182: return fullName;
183: }
184:
185: /**
186: * Not the same toString as JUnit, instead use a javadoc style syntax.
187: *
188: * @see java.lang.Object#toString()
189: */
190: public String toString() {
191: return this .getClass().getName() + "#" + fullName;
192: }
193:
194: /**
195: * The appropriate behaviour for this test case instance, given the name.
196: * <p>
197: * This method should not be used or overridden, it is strictly internal in
198: * DDSteps.
199: *
200: * @return A behaviour, never null.
201: */
202: DdBehaviour getBehaviour() {
203:
204: // Lazy creating
205: if (behaviour == null) {
206: if (StringUtils.isBlank(fullName)) {
207: throw new IllegalStateException(
208: "setName() has not been called. This method cannot be used before setName().");
209: }
210:
211: // Get dataloader from subclass
212: DataLoader dataLoader = createDataLoader();
213:
214: if (dataLoader == null) {
215: throw new IllegalStateException(
216: "Your implementation of the abstract method createDataLoader() returned null. Don't do that!");
217: }
218:
219: // Get the right behaviour
220: behaviour = StaticBehaviourFactory.getBehaviour(this ,
221: dataLoader);
222: }
223:
224: return behaviour;
225:
226: }
227:
228: // ///////////////////////////////////////////////////////////
229: // Abstract Factory Methods
230: // ///////////////////////////////////////////////////////////
231:
232: /**
233: * Factory Method: You as a user of DDSteps must implement this. You should
234: * return the data loader that you want to use to load the test data.
235: * Usually, a DataLoaders would be stateless, so not creating a new instance
236: * every time, but rather using a single instance would make sense. The
237: * stateless dataloaders provided by DDSteps have an easy-to-use INSTANCE
238: * static field, please use that.
239: * <p>
240: * It is recommended that you yourself create one (or more) base class for
241: * all your test cases, and implement this method there, as you will
242: * probably use the same kind of data loader for all your DDSteps testcases.
243: * <p>
244: * In fact, there are even some utility base classes for you, so that you
245: * don't subclass this class directly.
246: * <p>
247: * TODO Add see to util classes
248: *
249: * @return Don't return null.
250: * @see org.ddsteps.data.support.DataLoaderFactory#getCachingExcelDataLoader()
251: */
252: protected abstract DataLoader createDataLoader();
253:
254: // ///////////////////////////////////////////////////////////
255: // Empty Default Implementations
256: // ///////////////////////////////////////////////////////////
257:
258: /**
259: * Override this in your subclass, if desired.
260: *
261: * @throws Exception
262: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpMethod()
263: */
264: public void setUpMethod() throws Exception {
265: }
266:
267: /**
268: * Override this in your subclass, if desired.
269: *
270: * @throws Exception
271: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpBeforeData()
272: */
273: public void setUpBeforeData() throws Exception {
274: }
275:
276: /**
277: * Override this in your subclass, if desired.
278: *
279: * @throws Exception
280: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpAfterData()
281: */
282: public void setUpAfterData() throws Exception {
283: }
284:
285: /**
286: * Override this in your subclass, if desired.
287: *
288: * @throws Exception
289: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownBeforeData()
290: */
291: public void tearDownBeforeData() throws Exception {
292: }
293:
294: /**
295: * Override this in your subclass, if desired.
296: *
297: * @throws Exception
298: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownAfterData()
299: */
300: public void tearDownAfterData() throws Exception {
301: }
302:
303: /**
304: * Override this in your subclass, if desired.
305: *
306: * @throws Exception
307: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownMethod()
308: */
309: public void tearDownMethod() throws Exception {
310: }
311:
312: }
|