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.spring;
018:
019: import junit.framework.TestResult;
020:
021: import org.ddsteps.data.DataLoader;
022: import org.ddsteps.junit.behaviour.DdBehaviour;
023: import org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler;
024: import org.ddsteps.junit.behaviour.DdTestCaseHooks;
025: import org.ddsteps.junit.behaviour.StaticBehaviourFactory;
026: import org.ddsteps.util.TestCaseInfo;
027: import org.ddsteps.util.TestCaseNameUtils;
028: import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
029:
030: /**
031: * Spring Dependency Injection TestCase that is data driven.
032: * <p>
033: * You need the following stuff in your context:
034: * <p>
035: * By default, a context xml file called "ddsteps-context.xml" in the root of
036: * your classpath.
037: * <p>
038: * By default, the context is only loaded once, so you can probably use the same
039: * context for all your test, even if you don't happen to need a datasource in
040: * every test.
041: * <p>
042: * Known drawbacks when using this class:
043: * <ul>
044: * <li>The test case count is not correct, as the behaviour is not available
045: * until after setup, so it just returns 1.</li>
046: * </ul>
047: *
048: * @author Adam Skogman
049: * @since 1.1
050: * @version $Id$
051: */
052: public abstract class DDStepsSpringTestCase extends
053: AbstractDependencyInjectionSpringContextTests implements
054: DdTestCaseHooks {
055:
056: /**
057: * Constant: The default bean name for your DataLoader
058: */
059: public static final String DEFAULT_BEAN_NAME_DATA_LOADER = "dataLoader";
060:
061: /**
062: * Constant: The default spring context loaded by DDSteps.
063: */
064: public static final String DEFAULT_CONTEXT_XML = "ddsteps-context.xml";
065:
066: /**
067: * Delegate: The Behaviour delegate.
068: * <p>
069: * This will not be dependency injected by default, as there is no setter
070: * and the field is not protected.
071: * <p>
072: * Always get the behaviour using the getBehaviour() method.
073: */
074: DdBehaviour behaviour;
075:
076: /**
077: * Property: The FULL name of this data driven testcase, i.e. testMethod[Row
078: * 13] if a row instance.
079: */
080: String fullName;
081:
082: /**
083: * Inner class, will simply delegate all calls back to the parent. Needed
084: * since setUp() and tearDown() are not public in Springs testcase.
085: */
086: protected final DdBehaviourCallbackHandler callbackHandler = new SpringDdBehaviourCallbackHandler(
087: this );
088:
089: /**
090: * Force a default constructor, so that subclasses don't have to mess with
091: * the name. JUnit will always call setName(), well, TestSuite will do that
092: * actually.
093: */
094: public DDStepsSpringTestCase() {
095: super ();
096: }
097:
098: // ////////////////////////////////////////////////////////////////////////
099: // NAME and BEHAVIOUR HANDLING
100: // ////////////////////////////////////////////////////////////////////////
101:
102: /**
103: * Overrides the name setter. Behaviour is cleared when name is reset.
104: *
105: * @see junit.framework.TestCase#setName(java.lang.String)
106: */
107: public final void setName(String name) {
108:
109: // Save the full name
110: this .fullName = name;
111:
112: TestCaseInfo info = TestCaseNameUtils.parseTestCaseName(name);
113:
114: // TestCase only gets the method
115: super .setName(info.getMethodName());
116:
117: // Behaviour gets loaded when needed
118: behaviour = null;
119: }
120:
121: /**
122: * Returns the FULL data driven testcase name.
123: *
124: * @return A string, null if setName() has not been called.
125: */
126: public String getName() {
127: return fullName;
128: }
129:
130: /**
131: * Get the behaviour for this instance. The behaviour is cached, once
132: * loaded.
133: * <p>
134: * When getting the behaviour, the dataloader will be looked up from the
135: * spring context.
136: *
137: * @return A behaviour, never null.
138: */
139: DdBehaviour getBehaviour() {
140:
141: if (this .behaviour == null) {
142:
143: // Get the application context here, already
144: if (this .applicationContext == null) {
145: this .applicationContext = getContext(contextKey());
146: }
147:
148: // Get data loader
149: String dataLoaderName = getDataLoaderBeanName();
150:
151: DataLoader dataLoader = (DataLoader) applicationContext
152: .getBean(dataLoaderName, DataLoader.class);
153:
154: // Get behaviour from factory
155: this .behaviour = StaticBehaviourFactory.getBehaviour(this ,
156: dataLoader);
157:
158: }
159:
160: return this .behaviour;
161:
162: }
163:
164: // ////////////////////////////////////////////////////////////////////////
165: // OVERRIDES, delegating to Behaviour
166: // ////////////////////////////////////////////////////////////////////////
167:
168: /**
169: * Override, delegates to behaviour.
170: *
171: * @see junit.framework.Test#run(junit.framework.TestResult)
172: */
173: final public void run(TestResult result) {
174: if (getBehaviour().run(result, callbackHandler)) {
175: super .run(result);
176: }
177: }
178:
179: /**
180: * When we get here, the dependency injection will have happened.
181: *
182: * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#onSetUp()
183: */
184: final protected void onSetUp() throws Exception {
185: getBehaviour().setUp(callbackHandler);
186: }
187:
188: /**
189: * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#onTearDown()
190: */
191: final protected void onTearDown() throws Exception {
192: getBehaviour().tearDown(callbackHandler);
193: }
194:
195: /**
196: * @see junit.framework.Test#countTestCases()
197: */
198: final public int countTestCases() {
199: return getBehaviour().countTestCases();
200: }
201:
202: // ////////////////////////////////////////////////////////////////////////
203: // Exposing setUp() and tearDown()
204: // ////////////////////////////////////////////////////////////////////////
205:
206: /**
207: * Expose the setUp in the superclass for DdBehaviour callback to use.
208: *
209: * @throws Exception
210: */
211: final void super SetUp() throws Exception {
212: setUp();
213: }
214:
215: /**
216: * Expose the tearDown in the superclass for DdBehaviour callback to use.
217: *
218: * @throws Exception
219: */
220: final void super TearDown() throws Exception {
221: tearDown();
222: }
223:
224: // ////////////////////////////////////////////////////////////////////////
225: // CONFIG METHODS, may be overridden in subclass
226: // ////////////////////////////////////////////////////////////////////////
227:
228: /**
229: * Subclasses may override this if they don't want their DataLoader pulled
230: * from the context by the name of "dataLoader".
231: *
232: * @return Bean name of the DataLoader.
233: */
234: protected String getDataLoaderBeanName() {
235: return DEFAULT_BEAN_NAME_DATA_LOADER;
236: }
237:
238: /**
239: * Default is to load the context from ddsteps-config.xml in the root of the
240: * classpath.
241: *
242: * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
243: */
244: protected String[] getConfigLocations() {
245: return new String[] { DEFAULT_CONTEXT_XML };
246: }
247:
248: // ////////////////////////////////////////////////////////////////////////
249: // CALLBACK METHODS, default implementations, may be overridden in
250: // subcalsses
251: // ////////////////////////////////////////////////////////////////////////
252:
253: /**
254: * Override this in your subclass, if desired.
255: *
256: * @throws Exception
257: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpMethod()
258: */
259: public void setUpMethod() throws Exception {
260: }
261:
262: /**
263: * Override this in your subclass, if desired.
264: *
265: * @throws Exception
266: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpBeforeData()
267: */
268: public void setUpBeforeData() throws Exception {
269: }
270:
271: /**
272: * Override this in your subclass, if desired.
273: *
274: * @throws Exception
275: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#setUpAfterData()
276: */
277: public void setUpAfterData() throws Exception {
278: }
279:
280: /**
281: * Override this in your subclass, if desired.
282: *
283: * @throws Exception
284: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownBeforeData()
285: */
286: public void tearDownBeforeData() throws Exception {
287: }
288:
289: /**
290: * Override this in your subclass, if desired.
291: *
292: * @throws Exception
293: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownAfterData()
294: */
295: public void tearDownAfterData() throws Exception {
296: }
297:
298: /**
299: * Override this in your subclass, if desired.
300: *
301: * @throws Exception
302: * @see org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler#tearDownMethod()
303: */
304: public void tearDownMethod() throws Exception {
305: }
306:
307: }
|