001 /*
002 * Created on Apr 22, 2007
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. 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 distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 *
014 * Copyright @2007 the original author or authors.
015 */
016 package org.fest.mocks;
017
018 import java.lang.reflect.Proxy;
019 import java.util.ArrayList;
020 import java.util.List;
021
022 import net.sf.cglib.proxy.Enhancer;
023 import static org.easymock.classextension.EasyMock.*;
024
025 /**
026 * Understands a template for usage of <a href="http://www.easymock.org/" target="_blank">EasyMock</a> mocks.
027 * <p>
028 * Here is an small example that uses <a href="http://www.easymock.org/" target="_blank">EasyMock</a> to verify that
029 * <code>EmployeeBO</code> uses a <code>EmployeeDAO</code> to store employee information in the database:
030 * <pre>
031 * <code>
032 * @Test public void shouldAddNewEmployee() {
033 * mockEmployeeDao.insert(employee);
034 * replay(mockEmployeeDao);
035 * employeeBo.addNewEmployee(employee);
036 * verify(mockEmployeeDao);
037 * }
038 * </code>
039 * </pre>
040 * </p>
041 * <p>
042 * In this example, it is easy to distinguish the expectations made on the mock object (<code>mockEmployeeDao</code>).
043 * But in a more complex scenario, that distinction could be more difficult to spot. We can use the
044 * <code>EasyMockTemplate</code> to separate the code to be tested from the mock expectations:
045 * <pre>
046 * <code>
047 * @Test public void shouldAddNewEmployee() {
048 * EasyMockTemplate t = new EasyMockTemplate(mockEmployeeDao) {
049 * @Override protected void expectations() {
050 * mockEmployeeDao.insert(employee);
051 * }
052 *
053 * @Override protected void codeToTest() {
054 * employeeBo.addNewEmployee(employee);
055 * }
056 * };
057 * t.run();
058 * }
059 * </code>
060 * </pre>
061 * </p>
062 * <p>
063 * The benefits of <code>EasyMockTemplate</code> are:
064 * <ul>
065 * <li>Clear separation of mock expectations and code to test</li>
066 * <li>Less code duplication (we don't have to call <code>replay</code> and <code>verify</code> anymore)</li>
067 * </ul>
068 * </p>
069 *
070 * @author Alex Ruiz
071 */
072 public abstract class EasyMockTemplate {
073
074 /** Mock objects managed by this template */
075 private final List<Object> mocks = new ArrayList<Object>();
076
077 /**
078 * Constructor.
079 * @param mocks the mocks for this template to manage.
080 * @throws IllegalArgumentException if the list of mock objects is <code>null</code> or empty.
081 * @throws IllegalArgumentException if any of the given mocks is <code>null</code>.
082 * @throws IllegalArgumentException if any of the given mocks is not a mock.
083 */
084 public EasyMockTemplate(Object... mocks) {
085 if (mocks == null) throw new IllegalArgumentException("The list of mock objects should not be null");
086 if (mocks.length == 0) throw new IllegalArgumentException("The list of mock objects should not be empty");
087 for (Object mock : mocks) {
088 if (mock == null) throw new IllegalArgumentException("The list of mocks should not include null values");
089 this.mocks.add(checkAndReturnMock(mock));
090 }
091 }
092
093 private Object checkAndReturnMock(Object mock) {
094 if (Enhancer.isEnhanced(mock.getClass())) return mock;
095 if (Proxy.isProxyClass(mock.getClass())) return mock;
096 throw new IllegalArgumentException(mock + " is not a mock");
097 }
098
099 /**
100 * Encapsulates EasyMock's behavior pattern.
101 * <ol>
102 * <li>Set up expectations on the mock objects</li>
103 * <li>Set the state of the mock controls to "replay"</li>
104 * <li>Execute the code to test</li>
105 * <li>Verify that the expectations were met</li>
106 * </ol>
107 * Steps 2 and 4 are considered invariant behavior while steps 1 and 3 should be implemented by subclasses of this
108 * template.
109 * @throws UnexpectedError wrapping any checked exception thrown during the execution of this method.
110 */
111 public final void run() {
112 try {
113 setUp();
114 expectations();
115 for (Object mock : mocks) replay(mock);
116 codeToTest();
117 for (Object mock : mocks) verify(mock);
118 cleanUp();
119 } catch (Throwable t) {
120 if (t instanceof RuntimeException) throw (RuntimeException)t;
121 throw new UnexpectedError(t);
122 }
123 }
124
125 /**
126 * Returns all the mocks managed by this template.
127 * @return all the mocks managed by this template.
128 */
129 protected final List<Object> mocks() {
130 return new ArrayList<Object>(mocks);
131 }
132
133 /**
134 * Sets the expectations on the mock objects.
135 * @throws Throwable any error thrown by the implementation of this method.
136 */
137 protected abstract void expectations() throws Throwable;
138
139 /**
140 * Executes the code that is under test.
141 * @throws Throwable any error thrown by the implementation of this method.
142 */
143 protected abstract void codeToTest() throws Throwable;
144
145 /**
146 * Sets up the test fixture if necessary.
147 * @throws Throwable any error thrown by the implementation of this method.
148 */
149 protected void setUp() throws Throwable {}
150
151 /**
152 * Cleans up any resources if necessary.
153 * @throws Throwable any error thrown by the implementation of this method.
154 */
155 protected void cleanUp() throws Throwable {}
156 }