001    /*
002     * Copyright 2013 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwtmockito;
017    
018    import static org.mockito.Mockito.mock;
019    
020    import com.google.gwt.core.client.GWT;
021    import com.google.gwt.core.client.GWTBridge;
022    import com.google.gwt.i18n.client.Messages;
023    import com.google.gwt.resources.client.ClientBundle;
024    import com.google.gwt.resources.client.CssResource;
025    import com.google.gwt.safehtml.client.SafeHtmlTemplates;
026    import com.google.gwt.uibinder.client.UiBinder;
027    import com.google.gwt.user.client.rpc.RemoteService;
028    import com.google.gwtmockito.fakes.FakeClientBundleProvider;
029    import com.google.gwtmockito.fakes.FakeMessagesProvider;
030    import com.google.gwtmockito.fakes.FakeProvider;
031    import com.google.gwtmockito.fakes.FakeUiBinderProvider;
032    import com.google.gwtmockito.impl.ReturnsCustomMocks;
033    
034    import org.mockito.Mockito;
035    import org.mockito.MockitoAnnotations;
036    
037    import java.lang.reflect.Field;
038    import java.lang.reflect.InvocationTargetException;
039    import java.lang.reflect.Method;
040    import java.util.HashMap;
041    import java.util.Map;
042    import java.util.Map.Entry;
043    
044    /**
045     * A library to make Mockito-based testing of GWT applications easier. Most
046     * users won't have to reference this class directly and should instead use
047     * {@link GwtMockitoTestRunner}. Users who cannot use that class (e.g. tests
048     * using JUnit3) can invoke {@link #initMocks} directly in their setUp and
049     * {@link #tearDown} in their tearDown methods.
050     * <p>
051     * Note that calling {@link #initMocks} and {@link #tearDown} directly does
052     * <i>not</i> implement {@link GwtMockitoTestRunner}'s behavior of implementing
053     * native methods and making final methods mockable. The only way to get this
054     * behavior is by using {@link GwtMockitoTestRunner}.
055     * <p>
056     * Once {@link #initMocks} has been invoked, test code can safely call
057     * GWT.create without exceptions. Doing so will return either a mock object
058     * registered with {@link GwtMock}, a fake object specified by a call to
059     * {@link #useProviderForType}, or a new mock instance if no other binding
060     * exists. Fakes for types extending the following are provided by default:
061     * <ul>
062     *   <li> UiBinder: uses a fake that populates all UiFields with GWT.create'd
063     *        widgets, allowing them to be mocked like other calls to GWT.create.
064     *        See {@link FakeUiBinderProvider} for details.
065     *   <li> ClientBundle: Uses a fake that will return fake CssResources as
066     *        defined below, and will return fake versions of other resources that
067     *        return unique strings for getText and getSafeUri. See
068     *        {@link FakeClientBundleProvider} for details.
069     *   <li> Messages, CssResource, and SafeHtmlTemplates: uses a fake that
070     *        implements each method by returning a String of SafeHtml based on the
071     *        name of the method and any arguments passed to it. The exact format is
072     *        undefined. See {@link FakeMessagesProvider} for details.
073     * </ul>
074     * <p>
075     * The type returned from GWT.create will generally be the same as the type
076     * passed in. The exception is when GWT.create'ing a subclass of
077     * {@link RemoteService} - in this case, the result of GWT.create will be the
078     * Async version of that interface as defined by gwt-rpc.
079     * <p>
080     * If {@link #initMocks} is called manually, it is important to invoke
081     * {@link #tearDown} once the test has been completed. Failure to do so can
082     * cause state to leak between tests.
083     *
084     * @see GwtMockitoTestRunner
085     * @see GwtMock
086     * @author ekuefler@google.com (Erik Kuefler)
087     */
088    public class GwtMockito {
089    
090      private static Bridge bridge;
091    
092      /**
093       * Causes all calls to GWT.create to be intercepted to return a mock or fake
094       * object, and populates any {@link GwtMock}-annotated fields with mockito
095       * mocks. This method should be usually be called during the setUp method of a
096       * test case. Note that it explicitly calls
097       * {@link MockitoAnnotations#initMocks}, so there is no need to call that
098       * method separately. See the class description for more details.
099       *
100       * @param owner class to scan for {@link GwtMock}-annotated fields - almost
101       *              always "this" in unit tests
102       */
103      public static void initMocks(Object owner) {
104        // Create a new bridge and register built-in type providers
105        bridge = new Bridge();
106        useProviderForType(ClientBundle.class, new FakeClientBundleProvider());
107        useProviderForType(CssResource.class, new FakeMessagesProvider<CssResource>());
108        useProviderForType(Messages.class, new FakeMessagesProvider<Messages>());
109        useProviderForType(SafeHtmlTemplates.class, new FakeMessagesProvider<SafeHtmlTemplates>());
110        useProviderForType(UiBinder.class, new FakeUiBinderProvider());
111    
112        // Install the bridge and populate mock fields
113        boolean success = false;
114        try {
115          setGwtBridge(bridge);
116          registerGwtMocks(owner);
117          MockitoAnnotations.initMocks(owner);
118          success = true;
119        } finally {
120          if (!success) {
121            tearDown();
122          }
123        }
124      }
125    
126      /**
127       * Resets GWT.create to its default behavior. This method should be called
128       * after any test that called initMocks completes, usually in your test's
129       * tearDown method. Failure to do so can introduce unexpected ordering
130       * dependencies in tests.
131       */
132      public static void tearDown() {
133        setGwtBridge(null);
134      }
135    
136      /**
137       * Specifies that the given provider should be used to GWT.create instances of
138       * the given type and its subclasses. Note that if you just want to return a
139       * Mockito mock from GWT.create, it's probably easier to use {@link GwtMock}
140       * instead.
141       */
142      public static void useProviderForType(Class<?> type, FakeProvider<?> provider) {
143        if (bridge == null) {
144          throw new IllegalStateException("Must call initMocks() before calling useProviderForType()");
145        }
146        if (bridge.registeredMocks.containsKey(type)) {
147          throw new IllegalArgumentException(
148              "Can't use a provider for a type that already has a @GwtMock declared");
149        }
150        bridge.registeredProviders.put(type, provider);
151      }
152    
153      private static void registerGwtMocks(Object owner) {
154        Class<? extends Object> clazz = owner.getClass();
155    
156        while (!"java.lang.Object".equals(clazz.getName())) {
157          for (Field field : clazz.getDeclaredFields()) {
158            if (field.isAnnotationPresent(GwtMock.class)) {
159              Object mock = Mockito.mock(field.getType());
160              if (bridge.registeredMocks.containsKey(field.getType())) {
161                throw new IllegalArgumentException("Owner declares multiple @GwtMocks for type "
162                    + field.getType().getSimpleName() + "; only one is allowed");
163              }
164              bridge.registeredMocks.put(field.getType(), mock);
165              field.setAccessible(true);
166              try {
167                field.set(owner, mock);
168              } catch (IllegalAccessException e) {
169                throw new IllegalStateException("Failed to make field accessible: " + field);
170              }
171            }
172          }
173    
174          clazz = clazz.getSuperclass();
175        }
176      }
177    
178      private static void setGwtBridge(GWTBridge bridge) {
179        try {
180          Method setBridge = GWT.class.getDeclaredMethod("setBridge", GWTBridge.class);
181          setBridge.setAccessible(true);
182          setBridge.invoke(null, bridge);
183        } catch (SecurityException e) {
184          throw new RuntimeException(e);
185        } catch (InvocationTargetException e) {
186          throw new RuntimeException(e.getCause());
187        } catch (IllegalAccessException e) {
188          throw new AssertionError("Impossible since setBridge was made accessible");
189        } catch (NoSuchMethodException e) {
190          throw new AssertionError("Impossible since setBridge is known to exist");
191        }
192      }
193    
194      private static class Bridge extends GWTBridge {
195        private final Map<Class<?>, FakeProvider<?>> registeredProviders =
196            new HashMap<Class<?>, FakeProvider<?>>();
197        private final Map<Class<?>, Object> registeredMocks = new HashMap<Class<?>, Object>();
198    
199        @Override
200        @SuppressWarnings("unchecked") // safe since we check whether the type is assignable
201        public <T> T create(Class<?> type) {
202          // Handle RemoteServices specially - GWT.create'ing them should return the Async version
203          if (RemoteService.class.isAssignableFrom(type)) {
204            Class<?> asyncType;
205            try {
206              asyncType = Class.forName(type.getCanonicalName() + "Async");
207            } catch (ClassNotFoundException e) {
208              throw new IllegalArgumentException(
209                  type.getCanonicalName() + " does not have a corresponding async interface", e);
210            }
211            if (registeredMocks.containsKey(asyncType)) {
212              return (T) registeredMocks.get(asyncType);
213            } else {
214              return (T) mock(asyncType);
215            }
216          }
217    
218          // Otherwise, first check if we have a GwtMock for this exact type and use it if so.
219          if (registeredMocks.containsKey(type)) {
220            return (T) registeredMocks.get(type);
221          }
222    
223          // Next see if we have a provider for this type or a supertype.
224          for (Entry<Class<?>, FakeProvider<?>> entry : registeredProviders.entrySet()) {
225            if (entry.getKey().isAssignableFrom(type)) {
226              // We know this is safe since we just checked that the type can be assigned to the entry
227              @SuppressWarnings({"rawtypes", "cast"})
228              Class rawType = (Class) type;
229              return (T) entry.getValue().getFake(rawType);
230            }
231          }
232    
233          // If nothing has been registered, just return a new mock object to avoid NPEs.
234          return (T) mock(type, new ReturnsCustomMocks());
235        }
236    
237        @Override
238        public String getVersion() {
239          return getClass().getName();
240        }
241    
242        @Override
243        public boolean isClient() {
244          return false;
245        }
246    
247        @Override
248        public void log(String message, Throwable e) {
249          System.err.println(message + "\n");
250          if (e != null) {
251            e.printStackTrace();
252          }
253        }
254      }
255    }