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