001: /* DdRowBehaviour.java
002: *
003: * DDSteps - Data Driven JUnit Test Steps
004: * Copyright (C) 2005 Jayway AB
005: * www.ddsteps.org
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License version 2.1 as published by the Free Software Foundation.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, visit
018: * http://www.opensource.org/licenses/lgpl-license.php
019: */
020:
021: package org.ddsteps.junit.behaviour;
022:
023: import java.beans.PropertyEditor;
024: import java.util.Enumeration;
025: import java.util.Iterator;
026: import java.util.Properties;
027:
028: import junit.framework.TestCase;
029: import junit.framework.TestResult;
030:
031: import org.apache.commons.lang.Validate;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.ddsteps.beans.NumberPropertyEditor;
035: import org.ddsteps.beans.SqlDatePropertyEditor;
036: import org.ddsteps.beans.StringPropertyEditor;
037: import org.ddsteps.data.DataLoader;
038: import org.ddsteps.dataset.DataRow;
039: import org.ddsteps.dataset.DataValue;
040: import org.springframework.beans.BeanWrapper;
041: import org.springframework.beans.BeanWrapperImpl;
042: import org.springframework.beans.NotReadablePropertyException;
043: import org.springframework.beans.NotWritablePropertyException;
044:
045: /**
046: * Encapsulates the behaviour of the so called "row test case instance", which
047: * is the one that represents a row in the test data.
048: *
049: * @author adam
050: * @version $Id: DdRowBehaviour.java,v 1.3 2006/04/10 11:52:38 rickardnilsson Exp $
051: */
052: public class DdRowBehaviour extends DdBehaviourBase implements
053: DdBehaviour {
054:
055: /**
056: * Logger.
057: */
058: protected static final Log LOG = LogFactory
059: .getLog(DdRowBehaviour.class);
060:
061: /**
062: * Member: Id for this row.
063: */
064: protected String rowId;
065:
066: /**
067: * Property: Default properties.
068: */
069: protected Properties properties;
070:
071: /**
072: * Dependency Injection constructor.
073: *
074: * @param testCase
075: * The testcase itself.
076: * @param methodName
077: * The method name
078: * @param rowId
079: * The row id
080: * @param dataLoader
081: * The DataLoader
082: */
083: public DdRowBehaviour(TestCase testCase, String methodName,
084: String rowId, DataLoader dataLoader) {
085: super (testCase, methodName, dataLoader);
086:
087: Validate.notNull(rowId, "Argument rowId must not be null");
088: this .rowId = rowId;
089:
090: }
091:
092: /**
093: * Setup will callback, setup data, and callback.
094: * <p>
095: * TODO Better comment.
096: *
097: * @throws Exception
098: * @see org.ddsteps.junit.behaviour.DdBehaviour#setUp(DdBehaviourCallbackHandler)
099: */
100: public void setUp(DdBehaviourCallbackHandler ddTestCase)
101: throws Exception {
102:
103: // Hook: Before data setup
104: ddTestCase.setUpBeforeData();
105:
106: // Populate with data -----------------
107:
108: BeanWrapper beanWrapper = wrapBean(testCase);
109:
110: // TODO Remove properties once refactored into the dataloader
111: setUpProperties(this .properties, beanWrapper);
112:
113: DataRow row = dataLoader.loadRow(testCase, methodName, rowId);
114: setUpData(row, beanWrapper);
115:
116: // Hook: After data setup
117: ddTestCase.setUpAfterData();
118: }
119:
120: /**
121: * Wrap the testcase in some Spring goodness. Adds some new property editors
122: * which handle Number to Number conversions better.
123: *
124: * @param testCase
125: * The testcase to wrap
126: * @return A wrapper.
127: */
128: protected BeanWrapper wrapBean(TestCase testCase) {
129:
130: BeanWrapperImpl beanWrapperImpl = new BeanWrapperImpl(testCase);
131:
132: // All kinds of numbers
133: PropertyEditor byteEditor = new NumberPropertyEditor(
134: Byte.class, false);
135: PropertyEditor shortEditor = new NumberPropertyEditor(
136: Short.class, false);
137: PropertyEditor integerEditor = new NumberPropertyEditor(
138: Integer.class, false);
139: PropertyEditor longEditor = new NumberPropertyEditor(
140: Long.class, false);
141: PropertyEditor floatEditor = new NumberPropertyEditor(
142: Float.class, false);
143: PropertyEditor doubleEditor = new NumberPropertyEditor(
144: Double.class, false);
145:
146: beanWrapperImpl.registerCustomEditor(byte.class, byteEditor);
147: beanWrapperImpl.registerCustomEditor(Byte.class, byteEditor);
148:
149: beanWrapperImpl.registerCustomEditor(short.class, shortEditor);
150: beanWrapperImpl.registerCustomEditor(Short.class, shortEditor);
151:
152: beanWrapperImpl.registerCustomEditor(int.class, integerEditor);
153: beanWrapperImpl.registerCustomEditor(Integer.class,
154: integerEditor);
155:
156: beanWrapperImpl.registerCustomEditor(long.class, longEditor);
157: beanWrapperImpl.registerCustomEditor(Long.class, longEditor);
158:
159: beanWrapperImpl.registerCustomEditor(float.class, floatEditor);
160: beanWrapperImpl.registerCustomEditor(Float.class, floatEditor);
161:
162: beanWrapperImpl
163: .registerCustomEditor(double.class, doubleEditor);
164: beanWrapperImpl
165: .registerCustomEditor(Double.class, doubleEditor);
166:
167: // String editor
168: PropertyEditor stringEditor = new StringPropertyEditor();
169: beanWrapperImpl
170: .registerCustomEditor(String.class, stringEditor);
171:
172: // SQL dates
173: PropertyEditor sqlDateEditor = new SqlDatePropertyEditor();
174: beanWrapperImpl.registerCustomEditor(java.sql.Date.class,
175: sqlDateEditor);
176:
177: return beanWrapperImpl;
178: }
179:
180: /**
181: * Populates the testcase with data.
182: * <p>
183: * Uses the testcase taken as an arg to the constructor, and not the
184: * callback handler passed to setUp(), as the callback handler may not be
185: * the same object (typically, it may be an inner class).
186: *
187: * @param row
188: * The input row, not null.
189: * @param beanWrapper
190: * @throws Exception
191: * If population fails
192: */
193: protected void setUpData(DataRow row, BeanWrapper beanWrapper)
194: throws Exception {
195: Validate.notNull(row);
196:
197: for (Iterator iter = row.iterator(); iter.hasNext();) {
198:
199: DataValue dataValue = (DataValue) iter.next();
200:
201: if (LOG.isDebugEnabled()) {
202: LOG.debug("[populate] Data: Name '"
203: + dataValue.getName() + "' = Value '"
204: + dataValue.getValue() + "'");
205: }
206:
207: beanWrapper.setPropertyValue(dataValue.getName(), dataValue
208: .getValue());
209: }
210: }
211:
212: /**
213: * Populates this object with the data from the properties.
214: *
215: * @param properties
216: * The properties, null is ok.
217: * @param beanWrapper
218: * @throws Exception
219: * If population fails
220: */
221: protected void setUpProperties(Properties properties,
222: BeanWrapper beanWrapper) throws Exception {
223:
224: if (properties == null) {
225: return;
226: }
227:
228: // If we want to get EVERYTHING including defaults out of a properties
229: // object, we need to do like this, or else we don't get the defaults
230: // (delegate in Properties).
231: for (Enumeration propNameEnum = properties.propertyNames(); propNameEnum
232: .hasMoreElements();) {
233:
234: String key = (String) propNameEnum.nextElement();
235: Object value = properties.getProperty(key);
236:
237: if (LOG.isDebugEnabled()) {
238: LOG
239: .debug("[setUpProperties()] Properties Entry: Name '"
240: + key + "' = Value '" + value + "'");
241: }
242:
243: try {
244: beanWrapper.setPropertyValue(key, value);
245: } catch (NotReadablePropertyException e) {
246: LOG
247: .debug(
248: "Skipping property that could not be found in your test case.",
249: e);
250: } catch (NotWritablePropertyException e) {
251: LOG
252: .debug(
253: "Skipping property that could not be found in your test case.",
254: e);
255: }
256: }
257: }
258:
259: /**
260: * Teardown calls hooks.
261: *
262: * @throws Exception
263: * @see org.ddsteps.junit.behaviour.DdBehaviour#tearDown(DdBehaviourCallbackHandler)
264: */
265: public void tearDown(DdBehaviourCallbackHandler ddTestCase)
266: throws Exception {
267:
268: // Hook: Before data teardown
269: ddTestCase.tearDownBeforeData();
270:
271: // TODO Tear down data
272:
273: // TODO Hook: After data teardown
274: ddTestCase.tearDownAfterData();
275: }
276:
277: /**
278: * The row is just one testcase.
279: *
280: * @see org.ddsteps.junit.behaviour.DdBehaviour#countTestCases()
281: */
282: public int countTestCases() {
283: return 1;
284: }
285:
286: /**
287: * Always return true, this row instance should run just like a testcase.
288: *
289: * @see org.ddsteps.junit.behaviour.DdBehaviour#run(junit.framework.TestResult,
290: * org.ddsteps.junit.behaviour.DdBehaviourCallbackHandler)
291: */
292: public boolean run(TestResult result,
293: DdBehaviourCallbackHandler ddTestCase) {
294: return true;
295: }
296:
297: /**
298: * @return the properties
299: */
300: public Properties getProperties() {
301: return properties;
302: }
303:
304: /**
305: * @param properties
306: * the properties to set
307: */
308: public void setProperties(Properties properties) {
309: this.properties = properties;
310: }
311:
312: }
|