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     * &#64;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     * &#64;Test public void shouldAddNewEmployee() {
048     *   EasyMockTemplate t = new EasyMockTemplate(mockEmployeeDao) {
049     *     &#64;Override protected void expectations() {
050     *       mockEmployeeDao.insert(employee);
051     *     }
052     *
053     *     &#64;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    }