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 }