001 package org.apache.tapestry;
002
003 import org.apache.tapestry.test.Creator;
004 import static org.easymock.EasyMock.createStrictControl;
005 import org.easymock.IMocksControl;
006 import org.testng.Assert;
007 import org.testng.annotations.AfterMethod;
008
009 /**
010 * A base class for creating TestNG unit tests for Tapestry 4 applications. With slightly more
011 * effort, this may be used as a utility class with other frameworks, such as JUnit.
012 * <p>
013 * A single <em>strict</em> mock control is used for <strong>all</strong> mocks, which means that
014 * order of operations is checked not just for any single mock but across mocks.
015 * <p>
016 * Provides common mock factory and mock trainer methods.
017 * <p>
018 * Provides easy access to instantiated component instances.
019 * <p>
020 * Extends from {@link org.testng.Assert} to bring in all the public static assert methods without
021 * requiring extra imports.
022 * <p>
023 * TestNG supports running tests in parallel, as does this class. The EasyMock control is stored in
024 * a <em>thread local</em>. This is necessary as TestNG instantiates a single instance of the
025 * test case class, and invokes methods on it from multiple threads.
026 *
027 * @author Howard M. Lewis Ship
028 */
029 public class TestBase extends Assert
030 {
031 private static class ControlSource extends ThreadLocal<IMocksControl>
032 {
033 /** Creates a strick control for <em>this</em> thread. */
034 @Override
035 protected IMocksControl initialValue()
036 {
037 return createStrictControl();
038 }
039 }
040
041 private final ControlSource _source = new ControlSource();
042
043 // Access to this is synchronized.
044
045 private Creator _creator;
046
047 /**
048 * Creates a new instance of the provided class using the
049 * {@link org.apache.tapestry.test.Creator} utility.
050 *
051 * @param componentClass
052 * the component type to build
053 * @param properties
054 * alternating property names and property values to be injected into the instance
055 * @return the instantiated and configured component instance
056 */
057 public synchronized final <T> T newInstance(Class<T> componentClass, Object... properties)
058 {
059 if (_creator == null)
060 _creator = new Creator();
061
062 Object instance = _creator.newInstance(componentClass, properties);
063
064 return componentClass.cast(instance);
065 }
066
067 /**
068 * Discards any mock objects created during the test. When using TestBase as a utility class,
069 * not a base class, you must be careful to either invoke this method, or discard the TestBase
070 * instance at the end of each test.
071 */
072 @AfterMethod(alwaysRun = true)
073 public final void cleanupControlSource()
074 {
075 // TestNG reuses the same class instance across all tests within that
076 // class, so if we don't
077 // clear out the mocks, they will tend to accumulate. That can get
078 // expensive, and can
079 // cause unexpected cascade errors when an earlier test fails.
080
081 // After each method runs, we clear this thread's mocks control.
082 _source.remove();
083 }
084
085 /**
086 * Creates a new mock object of the indicated type. The created object is retained for the
087 * duration of the test (specifically to support {@link #replay()} and {@link #verify()}).
088 *
089 * @param <T>
090 * the type of the mock object
091 * @param mockClass
092 * the class to mock
093 * @return the mock object, ready for training
094 */
095 public final <T> T newMock(Class<T> mockClass)
096 {
097 return getMocksControl().createMock(mockClass);
098 }
099
100 /**
101 * Replay's the mocks control, preparing all mocks for testing.
102 */
103 public final void replay()
104 {
105 getMocksControl().replay();
106 }
107
108 /**
109 * Verifies the mocks control, ensuring that all mocks completed all trained method invocations,
110 * then resets the control to allow more training of the mocks.
111 */
112 public final void verify()
113 {
114 IMocksControl control = getMocksControl();
115
116 control.verify();
117 control.reset();
118 }
119
120 /**
121 * Returns the control object used for all mocks created by this test case.
122 *
123 * @return The {@link IMocksControl} used to manage mocks.
124 */
125 public final IMocksControl getMocksControl()
126 {
127 return _source.get();
128 }
129
130 /**
131 * Sets the return value for the most recent method call upon the mock.
132 *
133 * @param returnValue
134 * value to be returned from the method call
135 */
136 @SuppressWarnings("unchecked")
137 public final <T> void setReturnValue(T returnValue)
138 {
139 getMocksControl().andReturn(returnValue);
140 }
141
142 /**
143 * Trains a mock object to throw an exception.
144 *
145 * @param throwable
146 * the exception to be thrown by the most recent method call on the mock
147 */
148 public final void setThrowable(Throwable throwable)
149 {
150 getMocksControl().andThrow(throwable);
151 }
152
153 /**
154 * Invoked to indicate code should not reach a point. This is typically used after code that
155 * should throw an exception.
156 */
157 public final void unreachable()
158 {
159 fail("This code should not be reachable.");
160 }
161
162 /**
163 * Simply returns a new mock around {@link IRequestCycle}.
164 *
165 * @return The mocked request.
166 */
167 public final IRequestCycle newRequestCycle()
168 {
169 return newMock(IRequestCycle.class);
170 }
171
172 /**
173 * Creates a mock for {@link IMarkupWriter}.
174 *
175 * @return The mock.
176 */
177 public final IMarkupWriter newMarkupWriter()
178 {
179 return newMock(IMarkupWriter.class);
180 }
181 }