LittleMock.java revision ae4e4ac4facc3eacff13ab7ed115292fb5009d1e
1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.testing.littlemock; 18 19import java.io.File; 20import java.lang.reflect.Field; 21import java.lang.reflect.InvocationHandler; 22import java.lang.reflect.Method; 23import java.lang.reflect.Proxy; 24import java.util.ArrayList; 25import java.util.Arrays; 26import java.util.HashMap; 27import java.util.List; 28import java.util.Map; 29import java.util.concurrent.Callable; 30import java.util.concurrent.CopyOnWriteArrayList; 31import java.util.concurrent.atomic.AtomicReference; 32 33/** 34 * Very lightweight and simple mocking framework, inspired by Mockito, http://mockito.org. 35 * 36 * <p>It has a number of limitations, including: 37 * <ul> 38 * <li>Doesn't support mocking concrete classes, <b>interfaces only</b>.</li> 39 * 40 * <li>It supports only a <b>small subset</b> of the APIs provided by Mockito and other mocking 41 * frameworks.</li> 42 * </ul> 43 * 44 * <p>This project was designed to be lightweight and suitable for platforms that don't support 45 * dynamic class generation, for example Android. 46 * 47 * <p>Here is an example of how to use the framework. 48 * 49 * <p>Suppose that we have this interface: 50 * <pre> 51 * public interface Foo { 52 * public String aString(int input); 53 * public void doSomething(); 54 * } 55 * </pre> 56 * 57 * <p>Then stubbing out mocks works as follows: 58 * <pre> 59 * Foo mockFoo = mock(Foo.class); // Create the mock. 60 * doReturn("hello").when(mockFoo).aString(anyInt()); // Stub the mock to return "hello". 61 * doThrow(new IOException("oh noes")).when(mockFoo).doSomething(); 62 * assertEquals("hello", mockFoo.aString(5)); // Use the mock - performs as expected. 63 * mockFoo.doSomething(); // This will throw an IOException. 64 * </pre> 65 * 66 * <p>You can verify in the 'natural place', after the method call has happened, like this: 67 * <pre> 68 * Foo mockFoo = mock(Foo.class); 69 * assertEquals(null, mockFoo.aString(6)); // Unstubbed method calls return a sensible default. 70 * verify(mockFoo).aString(6); // This will pass, aString() was called once. 71 * verify(mockFoo, never()).doSomething(); // This will pass, doSomething was never called. 72 * verify(mockFoo, times(3)).aString(anyInt()); // This will fail, was called once only. 73 * </pre> 74 * 75 * <p>The documentation is still incomplete. You can look at the {@link LittleMockTest} class and 76 * its unit tests - since they tell you exactly what operations are supported and how to use them. 77 * 78 * <p>The reasons I much prefer Mockito's approach to the one of EasyMock are as follows: 79 * <ul> 80 * <li>No need to remember to put your mocks in replay mode.</li> 81 * <li>No need to remember to call verify, a dangerous source of false-positive tests or 82 * alternatively over-specified tests.</li> 83 * <li>Much less over-specification: only verify what you actually care about.</li> 84 * <li>Which in turn leads to better separated tests, each test verifying only a part of the 85 * desired behaviour.</li> 86 * <li>Which also leads to less fragile tests, where adding another method call on your 87 * dependencies doesn't break unrelated tests.</li> 88 * <li>More natural order for tests: set up stubs, execute tests, verify that it worked.</li> 89 * <li>More unified syntax that doesn't require special case for differences between void method 90 * calls and method calls that return a value.</li> 91 * </ul> 92 * 93 * <p>There were enough reasons that I wanted to give Mockito a try. It didn't work on Android 94 * because of issues with class generation. So I looked at the documentation and examples page and 95 * added tests for all the examples, and then implemented the this framework. I should stress that 96 * this is a clean-room implementation, and as such it's possible that there are a couple of methods 97 * that don't work in the same way as Mockito's implementation. Where that is the case I think we 98 * should fix once we discover them. There is also some functionality missing, but it will be added 99 * in due course. 100 * 101 * <p>Over time, the API has diverged slightly from the one of Mockito, as I have added APIs that I 102 * found convenient but that did not have an equivalent in Mockite itself. For anything that has an 103 * equivalent in Mockito I tried to keep the same name and syntax, to make it easier to transition 104 * between using one framework to using the other, e.g., when developing both an Android application 105 * using this framework and a desktop application using Mockito. 106 * 107 * @author hugohudson@gmail.com (Hugo Hudson) 108 */ 109/*@NotThreadSafe*/ 110public class LittleMock { 111 /** Generates a {@link Behaviour} suitable for void methods. */ 112 public static Behaviour doNothing() { return doReturn(null); } 113 114 /** Generates a {@link Behaviour} that returns the given result. */ 115 public static <T> Behaviour doReturn(final T result) { 116 return new BehaviourImpl(new Action() { 117 @Override public T doAction(Method method, Object[] args) { return result; } 118 @Override public Class<?> getReturnType() { 119 return (result == null) ? null : result.getClass(); 120 } 121 }); 122 } 123 124 /** 125 * Gets a {@link Behaviour} that will execute the given {@link Callable} and return its result. 126 */ 127 public static <T> Behaviour doAnswer(final Callable<T> callable) { 128 return new BehaviourImpl(new Action() { 129 @Override 130 public T doAction(Method method, Object[] args) throws Throwable { return callable.call(); } 131 @Override 132 public Class<?> getReturnType() { return null; } 133 }); 134 } 135 136 /** Returns a {@link Behaviour} that throws the given {@link Throwable}. */ 137 public static <T extends Throwable> Behaviour doThrow(final T exception) { 138 return new BehaviourImpl(new Action() { 139 @Override 140 public Object doAction(Method method, Object[] args) throws Throwable { throw exception; } 141 @Override 142 public Class<?> getReturnType() { return null; } 143 }); 144 } 145 146 /** Begins a verification step on a mock: the next method invocation on that mock will verify. */ 147 public static <T> T verify(T mock, CallCount howManyTimes) { 148 if (howManyTimes == null) { 149 throw new IllegalArgumentException("Can't pass null for howManyTimes parameter"); 150 } 151 DefaultInvocationHandler handler = getHandlerFrom(mock); 152 checkState(handler.mHowManyTimes == null, "Unfinished verify() statements"); 153 checkState(handler.mStubbingAction == null, "Unfinished stubbing statements"); 154 checkNoMatchers(); 155 handler.mHowManyTimes = howManyTimes; 156 sUnfinishedCallCounts.add(howManyTimes); 157 return handler.<T>getVerifyingMock(); 158 } 159 160 /** The list of outstanding calls to verify() that haven't finished, used to check for errors. */ 161 private static List<CallCount> sUnfinishedCallCounts = new ArrayList<CallCount>(); 162 163 /** The list of outstanding calls to when() that haven't finished, used to check for errors. */ 164 private static List<Action> sUnfinishedStubbingActions = new ArrayList<Action>(); 165 166 /** Begins a verification step for exactly one method call. */ 167 public static <T> T verify(T mock) { return verify(mock, times(1)); } 168 169 /** Assert that no method calls at all happened on these mocks. */ 170 public static void verifyZeroInteractions(Object... mocks) { 171 checkNoMatchers(); 172 for (Object mock : mocks) { 173 List<MethodCall> mMethodCalls = getHandlerFrom(mock).mRecordedCalls; 174 expect(mMethodCalls.isEmpty(), "Mock expected zero interactions, had " + mMethodCalls); 175 } 176 } 177 178 /** Assert that there are no unverified method calls on these mocks. */ 179 public static void verifyNoMoreInteractions(Object... mocks) { 180 StackTraceElement callSite = new Exception().getStackTrace()[1]; 181 for (Object mock : mocks) { 182 verifyNoMoreInteractions(mock, callSite); 183 } 184 } 185 186 /** Check that there are no unverified actions on the given mock. */ 187 private static void verifyNoMoreInteractions(Object mock, StackTraceElement callSite) { 188 checkNoMatchers(); 189 DefaultInvocationHandler handlerFrom = getHandlerFrom(mock); 190 List<MethodCall> unverifiedCalls = new ArrayList<MethodCall>(); 191 for (MethodCall method : handlerFrom.mRecordedCalls) { 192 if (!method.mWasVerified) { 193 unverifiedCalls.add(method); 194 } 195 } 196 if (unverifiedCalls.size() > 0) { 197 StringBuffer sb = new StringBuffer(); 198 sb.append("\nWe found these unverified calls:"); 199 for (MethodCall method : unverifiedCalls) { 200 appendDebugStringForMethodCall(sb, method.mMethod, 201 method.mElement, handlerFrom.mFieldName, false); 202 } 203 sb.append("\n\nAfter final interaction was verified:\n"); 204 sb.append(" at ").append(callSite).append("\n"); 205 throw new AssertionError(sb.toString()); 206 } 207 } 208 209 /** Creates a {@link CallCount} that matches exactly the given number of calls. */ 210 public static CallCount times(long n) { return new CallCount(n, n); } 211 212 /** Claims that the verified call must happen before the given timeout. */ 213 public static Timeout timeout(long timeoutMillis) { 214 return new Timeout(1, 1, timeoutMillis); 215 } 216 217/** Creates a {@link CallCount} that only matches if the method was never called. */ 218 public static CallCount never() { return new CallCount(0, 0); } 219 220 /** Creates a {@link CallCount} that matches at least one method call. */ 221 public static CallCount atLeastOnce() { return new CallCount(1, Long.MAX_VALUE); } 222 223 /** Creates a {@link CallCount} that matches any number of method calls, including none at all. */ 224 public static CallCount anyTimes() { return new CallCount(0, Long.MAX_VALUE); } 225 226 /** Creates a {@link CallCount} that matches at least the given number of calls. */ 227 public static CallCount atLeast(long n) { return new CallCount(n, Long.MAX_VALUE); } 228 229 /** Creates a {@link CallCount} that matches up to the given number of calls but no more. */ 230 public static CallCount atMost(long n) { return new CallCount(0, n); } 231 232 /** Creates a {@link CallCount} that matches any number of calls between the two given bounds. */ 233 public static CallCount between(long lower, long upper) { return new CallCount(lower, upper); } 234 235 /** 236 * Creates an argument matcher that matches any object, don't use for primitives. 237 * <p> 238 * <b>Note</b>: This does not enforce that the object is of type {@code T}; use 239 * {@link #isA(Class)} to do that. 240 */ 241 public static <T> T anyObject() { return LittleMock.<T>addMatcher(new MatchAnything(), null); } 242 243 /** Generates an argument matcher that matches any string. */ 244 public static String anyString() { return isA(String.class); } 245 246 /** Generates an argument matcher that matches any int. */ 247 public static int anyInt() { return addMatcher(new MatchAnything(), 0); } 248 249 /** Generates an argument matcher that matches any float. */ 250 public static float anyFloat() { return addMatcher(new MatchAnything(), 0f); } 251 252 /** Generates an argument matcher that matches any double. */ 253 public static double anyDouble() { return addMatcher(new MatchAnything(), 0.0); } 254 255 /** Generates an argument matcher that matches any boolean. */ 256 public static boolean anyBoolean() { return addMatcher(new MatchAnything(), false); } 257 258 /** Generates an argument matcher that matches any short. */ 259 public static short anyShort() { return addMatcher(new MatchAnything(), (short) 0); } 260 261 /** Generates an argument matcher that matches any char. */ 262 public static char anyChar() { return addMatcher(new MatchAnything(), '\u0000'); } 263 264 /** Generates an argument matcher that matches any long. */ 265 public static long anyLong() { return addMatcher(new MatchAnything(), 0L); } 266 267 /** Generates an argument matcher that matches any byte. */ 268 public static byte anyByte() { return addMatcher(new MatchAnything(), (byte) 0); } 269 270 /** Generates an argument matcher that matches exactly this value. */ 271 public static <T> T eq(final T expected) { 272 return addMatcher(new ArgumentMatcher() { 273 @Override 274 public boolean matches(Object value) { 275 return (expected == null) ? (value == null) : expected.equals(value); 276 } 277 }, expected); 278 } 279 280 /** An argument matcher that matches any value of the given type or a subtype thereof. */ 281 public static <T> T isA(final Class<T> clazz) { 282 return LittleMock.<T>addMatcher(new ArgumentMatcher() { 283 @Override 284 public boolean matches(Object value) { 285 return value == null || clazz.isAssignableFrom(value.getClass()); 286 } 287 }, null); 288 } 289 290 /** 291 * Injects fields annotated with {@link Mock} with a newly created mock, and those 292 * annotated with {@link Captor} with a suitable capture object. 293 * 294 * <p>This operation is recursive, and travels up the class hierarchy, in order to set all 295 * suitably annotated fields. 296 */ 297 public static void initMocks(Object instance) throws Exception { 298 injectMocksForClass(instance, instance.getClass()); 299 } 300 301 /** Recurse up the class hierarchy injecting mocks as we go, stopping when we reach Object. */ 302 private static void injectMocksForClass(Object instance, Class<?> clazz) 303 throws Exception { 304 if (clazz.equals(Object.class)) { 305 return; 306 } 307 for (Field field : clazz.getDeclaredFields()) { 308 if (field.getAnnotation(Mock.class) != null) { 309 setField(field, instance, mock(field.getType(), field.getName())); 310 } else if (field.getAnnotation(Captor.class) != null) { 311 setField(field, instance, createCaptor()); 312 } 313 } 314 injectMocksForClass(instance, clazz.getSuperclass()); 315 } 316 317 /** 318 * Creates a correctly typed {@link ArgumentCaptor} , it's easier to use 319 * {@link #initMocks(Object)}. 320 */ 321 public static <T> ArgumentCaptor<T> createCaptor() { 322 return new ArgumentCaptorImpl<T>(); 323 } 324 325 /** Implementation of the {@link ArgumentCaptor} interface. */ 326 private static class ArgumentCaptorImpl<T extends Object> implements ArgumentCaptor<T> { 327 private final ArrayList<T> mAllValues = new ArrayList<T>(); 328 private T mValue; 329 330 private ArgumentCaptorImpl() { 331 } 332 333 public void setValue(T value) { 334 mValue = value; 335 mAllValues.add(mValue); 336 } 337 338 @Override 339 public T getValue() { 340 return mValue; 341 } 342 343 @Override 344 public List<T> getAllValues() { 345 return mAllValues; 346 } 347 348 @Override 349 public T capture() { 350 return LittleMock.<T>addMatcher(this, null); 351 } 352 353 @Override 354 public boolean matches(Object value) { 355 // A capture always matches any argument. 356 // This is so that verify(mMock).someMethod(capture(mCapture)) will match any and all calls 357 // to someMethod() and we will capture the values into mCapture. 358 return true; 359 } 360 } 361 362 /** 363 * Creates a mock, more easily done via the {@link #initMocks(Object)} method. 364 * 365 * <p>Also if you use this method to create your mock, the field in the error messages will 366 * be named the same as your class passed in, you only get the actual field name if you 367 * use the annotation. 368 * 369 * @throws IllegalArgumentException if the class you pass in is null 370 */ 371 public static <T> T mock(Class<T> clazz) { 372 if (clazz == null) { 373 throw new IllegalArgumentException("Class to mock was null"); 374 } 375 return mock(clazz, getDefaultFieldNameFor(clazz)); 376 } 377 378 /** Creates a mock, more easily done via the {@link #initMocks(Object)} method. */ 379 @SuppressWarnings("unchecked") 380 private static <T> T mock(Class<T> clazz, String fieldName) { 381 return (T) createProxy(clazz, new DefaultInvocationHandler(clazz, fieldName)); 382 } 383 384 /** Pick a suitable name for a field of the given clazz. */ 385 private static String getDefaultFieldNameFor(Class<?> clazz) { 386 return clazz.getSimpleName().substring(0, 1).toLowerCase() 387 + clazz.getSimpleName().substring(1); 388 } 389 390 /** Clears out the expectations on these mocks. */ 391 public static void reset(Object... mocks) { 392 for (Object mock : mocks) { 393 getHandlerFrom(mock).reset(); 394 } 395 } 396 397 /** Use this in tear down to check for programming errors. */ 398 public static void checkForProgrammingErrorsDuringTearDown() { 399 checkNoMatchers(); 400 checkNoUnfinishedCalls(sUnfinishedCallCounts, "verify()"); 401 checkNoUnfinishedCalls(sUnfinishedStubbingActions, "stubbing"); 402 } 403 404 /** Helper function to check that there are no verify or stubbing commands outstanding. */ 405 private static void checkNoUnfinishedCalls(List<?> list, String type) { 406 if (!list.isEmpty()) { 407 list.clear(); 408 throw new IllegalStateException("Unfinished " + type + " statements"); 409 } 410 } 411 412 /** Implementation of {@link Behaviour}. */ 413 private static class BehaviourImpl implements Behaviour { 414 private final Action mAction; 415 416 private BehaviourImpl(Action action) { 417 mAction = action; 418 } 419 420 @Override 421 public <T> T when(T mock) { 422 DefaultInvocationHandler handler = getHandlerFrom(mock); 423 checkState(handler.mHowManyTimes == null, "Unfinished verify() statements"); 424 checkState(handler.mStubbingAction == null, "Unfinished stubbing statements"); 425 handler.mStubbingAction = mAction; 426 sUnfinishedStubbingActions.add(mAction); 427 return handler.<T>getStubbingMock(); 428 } 429 } 430 431 /** 432 * The static list of argument matchers, used in the next method call to the mock. 433 * 434 * <p>In order to support the syntax like this: verify(mFoo).someMethod(anyInt()), it is 435 * required that the anyInt() method store the value somewhere for use when the someMethod 436 * is invoked. That somewhere has to be static. I don't like it any more than you do. 437 * 438 * <p>The same goes for anything that is passed into the someMethod as an argument - be it 439 * a capture(mCaptureString) or eq(5) or whatever. 440 * 441 * <p>Avoiding the use of statics requires that we change the syntax of the verify statement, 442 * and I can't think of an elegant way of doing it, and in any case I really like the current 443 * syntax, so for now a static variable it is. 444 * 445 * <p>This match arguments list should contain either zero elements (the next method call will 446 * not use any argument matchers) or it should contain exactly one argument matcher for 447 * every argument being passed to the next method call. You can't mix argument matchers and 448 * raw values. 449 */ 450 private static final List<ArgumentMatcher> sMatchArguments = new ArrayList<ArgumentMatcher>(); 451 452 /** Encapsulates a single call of a method with associated arguments. */ 453 private static class MethodCall { 454 /** The method call. */ 455 private final Method mMethod; 456 /** The arguments provided at the time the call happened. */ 457 private final Object[] mArgs; 458 /** The line from the test that invoked the handler to create this method call. */ 459 private final StackTraceElement mElement; 460 /** Keeps track of method calls that have been verified, for verifyNoMoreInteractions(). */ 461 public boolean mWasVerified = false; 462 463 public MethodCall(Method method, StackTraceElement element, Object[] args) { 464 mMethod = method; 465 mElement = element; 466 mArgs = args; 467 } 468 469 public boolean argsMatch(Object[] args) { 470 return Arrays.equals(mArgs, args); 471 } 472 473 @Override 474 public String toString() { 475 return "MethodCall [method=" + mMethod + ", args=" + Arrays.toString(mArgs) + "]"; 476 } 477 } 478 479 /** 480 * Magically handles the invoking of method calls. 481 * 482 * <p>This object is in one of three states, default (where invoking methods returns default 483 * values and records the call), verifying (where invoking method calls makes sure that those 484 * method calls happen with the supplied arguments or matchers) or stubbing (where the next method 485 * call tells us which arguments to match in order to perform the desired behaviour). 486 */ 487 private static class DefaultInvocationHandler implements InvocationHandler { 488 private static Method sEqualsMethod; 489 private static Method sHashCodeMethod; 490 private static Method sToStringMethod; 491 492 static { 493 try { 494 sEqualsMethod = Object.class.getMethod("equals", Object.class); 495 sHashCodeMethod = Object.class.getMethod("hashCode"); 496 sToStringMethod = Object.class.getMethod("toString"); 497 } catch (SecurityException e) { 498 // Should never happen. 499 throw new RuntimeException("Your JVM/classloader is broken", e); 500 } catch (NoSuchMethodException e) { 501 // Should never happen. 502 throw new RuntimeException("Your JVM/classloader is broken", e); 503 } 504 } 505 506 /** The class of the mocked objects. */ 507 private final Class<?> mClazz; 508 /** The field name in which the mock is assigned. */ 509 private final String mFieldName; 510 511 /** The list of method calls executed on the mock. */ 512 private List<MethodCall> mRecordedCalls = new CopyOnWriteArrayList<MethodCall>(); 513 /** The list of method calls that were stubbed out and their corresponding actions. */ 514 private List<StubbedCall> mStubbedCalls = new CopyOnWriteArrayList<StubbedCall>(); 515 516 /** 517 * The number of times a given call should be verified. 518 * 519 * <p>It is not null when in the verification state, and it is actually used to determine if we 520 * are in the verification state. 521 * 522 * <p>It is reset to null once the verification has occurred. 523 */ 524 private CallCount mHowManyTimes = null; 525 526 /** 527 * The action to be associated with the stubbed method. 528 * 529 * <p>It is not null when in the stubbing state, and it is actually used to determine if we are 530 * in the stubbing state. 531 */ 532 private Action mStubbingAction = null; 533 534 /** Dynamic proxy used to verify calls against this mock. */ 535 private final Object mVerifyingMock; 536 537 /** Dynamic proxy used to stub calls against this mock. */ 538 private final Object mStubbingMock; 539 540 /** 541 * Creates a new invocation handler for an object. 542 * 543 * @param clazz the class the object belongs to 544 * @param fieldName The name to be used to refer to the object. This may either be the name of 545 * the field this mock will be stored into (in case it uses @Mock) or a suitable name to 546 * use to refer to the object in error messages, based on the name of the class itself. 547 */ 548 public DefaultInvocationHandler(Class<?> clazz, String fieldName) { 549 mClazz = clazz; 550 mFieldName = fieldName; 551 mVerifyingMock = createVerifyingMock(clazz); 552 mStubbingMock = createStubbingMock(clazz); 553 } 554 555 // Safe if you call getHandlerFrom(mock).getVerifyingMock(), since this is guaranteed to be 556 // of the same type as mock itself. 557 @SuppressWarnings("unchecked") 558 public <T> T getVerifyingMock() { 559 return (T) mVerifyingMock; 560 } 561 562 // Safe if you call getHandlerFrom(mock).getStubbingMock(), since this is guaranteed to be 563 // of the same type as mock itself. 564 @SuppressWarnings("unchecked") 565 public <T> T getStubbingMock() { 566 return (T) mStubbingMock; 567 } 568 569 /** Used to check that we always stub and verify from the same thread. */ 570 private AtomicReference<Thread> mCurrentThread = new AtomicReference<Thread>(); 571 572 /** Check that we are stubbing and verifying always from the same thread. */ 573 private void checkThread() { 574 Thread currentThread = Thread.currentThread(); 575 mCurrentThread.compareAndSet(null, currentThread); 576 if (mCurrentThread.get() != currentThread) { 577 throw new IllegalStateException("Must always mock and stub from one thread only. " 578 + "This thread: " + currentThread + ", the other thread: " + mCurrentThread.get()); 579 } 580 } 581 582 /** Generate the dynamic proxy that will handle verify invocations. */ 583 private Object createVerifyingMock(Class<?> clazz) { 584 return createProxy(clazz, new InvocationHandler() { 585 @Override 586 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 587 checkThread(); 588 expect(mHowManyTimes != null, "verifying mock doesn't know how many times"); 589 try { 590 ArgumentMatcher[] matchers = checkClearAndGetMatchers(method); 591 StackTraceElement callSite = new Exception().getStackTrace()[2]; 592 MethodCall methodCall = new MethodCall(method, callSite, args); 593 innerVerify(method, matchers, methodCall, proxy, callSite, mHowManyTimes); 594 return defaultReturnValue(method.getReturnType()); 595 } finally { 596 sUnfinishedCallCounts.remove(mHowManyTimes); 597 mHowManyTimes = null; 598 } 599 } 600 }); 601 } 602 603 /** Generate the dynamic proxy that will handle stubbing invocations. */ 604 private Object createStubbingMock(Class<?> clazz) { 605 return createProxy(clazz, new InvocationHandler() { 606 @Override 607 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 608 checkThread(); 609 expect(mStubbingAction != null, "stubbing mock doesn't know what action to perform"); 610 try { 611 ArgumentMatcher[] matchers = checkClearAndGetMatchers(method); 612 StackTraceElement callSite = new Exception().getStackTrace()[2]; 613 MethodCall methodCall = new MethodCall(method, callSite, args); 614 innerStub(method, matchers, methodCall, callSite, mStubbingAction); 615 return defaultReturnValue(method.getReturnType()); 616 } finally { 617 sUnfinishedStubbingActions.remove(mStubbingAction); 618 mStubbingAction = null; 619 } 620 } 621 }); 622 } 623 624 @Override 625 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 626 StackTraceElement callSite = new Exception().getStackTrace()[2]; 627 MethodCall methodCall = new MethodCall(method, callSite, args); 628 return innerRecord(method, args, methodCall, proxy, callSite); 629 } 630 631 /** 632 * Checks whether the given method is one of the special object methods that should not 633 * verified or stubbed. 634 * <p> 635 * If this is one of such methods, it throws an AssertionException. 636 * 637 * @param method the method to be checked 638 * @param operation the name of the operation, used for generating a helpful message 639 */ 640 private void checkSpecialObjectMethods(Method method, String operation) { 641 if (method.equals(sEqualsMethod) 642 || method.equals(sHashCodeMethod) 643 || method.equals(sToStringMethod)) { 644 fail("cannot " + operation + " call to " + method); 645 } 646 } 647 648 private void reset() { 649 mRecordedCalls.clear(); 650 mStubbedCalls.clear(); 651 mHowManyTimes = null; 652 mStubbingAction = null; 653 } 654 655 private Object innerRecord(Method method, final Object[] args, 656 MethodCall methodCall, Object proxy, StackTraceElement callSite) throws Throwable { 657 if (method.equals(sEqualsMethod)) { 658 // Use identify for equality, the default behavior on object. 659 return proxy == args[0]; 660 } else if (method.equals(sHashCodeMethod)) { 661 // This depends on the fact that each mock has its own DefaultInvocationHandler. 662 return hashCode(); 663 } else if (method.equals(sToStringMethod)) { 664 // This is used to identify this is a mock, e.g., in error messages. 665 return "Mock<" + mClazz.getName() + ">"; 666 } 667 mRecordedCalls.add(methodCall); 668 for (StubbedCall stubbedCall : mStubbedCalls) { 669 if (stubbedCall.mMethodCall.mMethod.equals(methodCall.mMethod)) { 670 if (stubbedCall.mMethodCall.argsMatch(methodCall.mArgs)) { 671 methodCall.mWasVerified = true; 672 return stubbedCall.mAction.doAction(method, args); 673 } 674 } 675 } 676 // If no stub is defined, return the default value. 677 return defaultReturnValue(method.getReturnType()); 678 } 679 680 private void innerStub(Method method, final ArgumentMatcher[] matchers, MethodCall methodCall, 681 StackTraceElement callSite, final Action stubbingAction) { 682 checkSpecialObjectMethods(method, "stub"); 683 checkThisActionCanBeUsedForThisMethod(method, stubbingAction, callSite); 684 if (matchers.length == 0) { 685 // If there are no matchers, then this is a simple stubbed method call with an action. 686 mStubbedCalls.add(0, new StubbedCall(methodCall, stubbingAction)); 687 return; 688 } 689 // If there are matchers, then we need to make a new method call which matches only 690 // when all the matchers match. Further, the action that method call needs to take 691 // is to first record the values into any captures that may be present, and only then 692 // proceed to execute the original action. 693 MethodCall matchMatchersMethodCall = new MethodCall(method, callSite, matchers) { 694 @Override 695 public boolean argsMatch(Object[] args) { return doMatchersMatch(matchers, args); } 696 }; 697 Action setCapturesThenAction = new Action() { 698 @Override 699 public Object doAction(Method innerMethod, Object[] innerArgs) throws Throwable { 700 setCaptures(matchers, innerArgs); 701 return stubbingAction.doAction(innerMethod, innerArgs); 702 } 703 @Override 704 public Class<?> getReturnType() { 705 return stubbingAction.getReturnType(); 706 } 707 }; 708 mStubbedCalls.add(0, new StubbedCall(matchMatchersMethodCall, setCapturesThenAction)); 709 } 710 711 private void checkThisActionCanBeUsedForThisMethod(Method method, final Action stubbingAction, 712 StackTraceElement callSite) { 713 Class<?> methodType = method.getReturnType(); 714 Class<?> actionType = stubbingAction.getReturnType(); 715 if (actionType == null) { 716 // We could not determine the type returned by this action at the time we 717 // created it. At this time we cannot check that the returned value is 718 // appropriate to the return type of the method. 719 // However, if the type is not correct, any actual invocation of the method 720 // will fail later on. 721 return; 722 } 723 if (!methodType.isAssignableFrom(actionType)) { 724 if (methodType.isPrimitive() && 725 actionType.equals(PRIMITIVE_TO_BOXED_LOOKUP.get(methodType))) { 726 return; 727 } 728 StringBuffer sb = new StringBuffer(); 729 sb.append("\nCan't return ").append(actionType.getSimpleName()).append(" from stub for:"); 730 appendDebugStringForMethodCall(sb, method, callSite, mFieldName, true); 731 sb.append("\n"); 732 throw new IllegalArgumentException(sb.toString()); 733 } 734 } 735 736 private boolean doMatchersMatch(ArgumentMatcher[] matchers, Object[] args) { 737 for (int i = 0; i < matchers.length; ++i) { 738 if (!matchers[i].matches(args[i])) { 739 return false; 740 } 741 } 742 return true; 743 } 744 745 private void innerVerify(Method method, ArgumentMatcher[] matchers, MethodCall methodCall, 746 Object proxy, StackTraceElement callSite, CallCount callCount) { 747 checkSpecialObjectMethods(method, "verify"); 748 int total = countMatchingInvocations(method, matchers, methodCall); 749 long callTimeout = callCount.getTimeout(); 750 if (callTimeout > 0) { 751 long endTime = System.currentTimeMillis() + callTimeout; 752 while (!callCount.matches(total)) { 753 try { 754 Thread.sleep(1); 755 } catch (InterruptedException e) { 756 fail("interrupted whilst waiting to verify"); 757 } 758 if (System.currentTimeMillis() > endTime) { 759 fail(formatFailedVerifyMessage(methodCall, total, callTimeout, callCount)); 760 } 761 total = countMatchingInvocations(method, matchers, methodCall); 762 } 763 } else { 764 if (!callCount.matches(total)) { 765 fail(formatFailedVerifyMessage(methodCall, total, 0, callCount)); 766 } 767 } 768 } 769 770 private int countMatchingInvocations(Method method, ArgumentMatcher[] matchers, 771 MethodCall methodCall) { 772 int total = 0; 773 for (MethodCall call : mRecordedCalls) { 774 if (call.mMethod.equals(method)) { 775 if ((matchers.length > 0 && doMatchersMatch(matchers, call.mArgs)) || 776 call.argsMatch(methodCall.mArgs)) { 777 setCaptures(matchers, call.mArgs); 778 ++total; 779 call.mWasVerified = true; 780 } 781 } 782 } 783 return total; 784 } 785 786 private String formatFailedVerifyMessage(MethodCall methodCall, int total, long timeoutMillis, 787 CallCount callCount) { 788 StringBuffer sb = new StringBuffer(); 789 sb.append("\nExpected ").append(callCount); 790 if (timeoutMillis > 0) { 791 sb.append(" within " + timeoutMillis + "ms"); 792 } 793 sb.append(" to:"); 794 appendDebugStringForMethodCall(sb, methodCall.mMethod, 795 methodCall.mElement, mFieldName, false); 796 sb.append("\n\n"); 797 if (mRecordedCalls.size() == 0) { 798 sb.append("No method calls happened on this mock"); 799 } else { 800 sb.append("Method calls that did happen:"); 801 for (MethodCall recordedCall : mRecordedCalls) { 802 appendDebugStringForMethodCall(sb, recordedCall.mMethod, 803 recordedCall.mElement, mFieldName, false); 804 } 805 } 806 sb.append("\n"); 807 return sb.toString(); 808 } 809 810 /** All matchers that are captures will store the corresponding arg value. */ 811 // This suppress warning means that I'm calling setValue with something that I can't promise 812 // is of the right type. But I think it is unavoidable. Certainly I could give a better 813 // error message than the class cast exception you'll get when you try to retrieve the value. 814 @SuppressWarnings("unchecked") 815 private void setCaptures(ArgumentMatcher[] matchers, Object[] args) { 816 for (int i = 0; i < matchers.length; ++i) { 817 if (matchers[i] instanceof ArgumentCaptorImpl) { 818 ArgumentCaptorImpl.class.cast(matchers[i]).setValue(args[i]); 819 } 820 } 821 } 822 823 /** An empty array of matchers, to optimise the toArray() call below. */ 824 private static final ArgumentMatcher[] EMPTY_MATCHERS_ARRAY = new ArgumentMatcher[0]; 825 826 /** Makes sure that we have the right number of MATCH_ARGUMENTS for the given method. */ 827 private ArgumentMatcher[] checkClearAndGetMatchers(Method method) { 828 ArgumentMatcher[] matchers = sMatchArguments.toArray(EMPTY_MATCHERS_ARRAY); 829 sMatchArguments.clear(); 830 if (matchers.length > 0 && method.getParameterTypes().length != matchers.length) { 831 throw new IllegalArgumentException("You can't mix matchers and regular objects."); 832 } 833 return matchers; 834 } 835 } 836 837 private static void appendDebugStringForMethodCall(StringBuffer sb, Method method, 838 StackTraceElement callSite, String fieldName, boolean showReturnType) { 839 sb.append("\n "); 840 if (showReturnType) { 841 sb.append("(").append(method.getReturnType().getSimpleName()).append(") "); 842 } 843 sb.append(fieldName).append(".").append(method.getName()).append("("); 844 int i = 0; 845 for (Class<?> type : method.getParameterTypes()) { 846 if (++i > 1) { 847 sb.append(", "); 848 } 849 sb.append(type.getSimpleName()); 850 } 851 sb.append(")\n at ").append(callSite); 852 } 853 854 /** Call this function when you don't expect there to be any outstanding matcher objects. */ 855 private static void checkNoMatchers() { 856 if (sMatchArguments.size() > 0) { 857 sMatchArguments.clear(); 858 throw new IllegalStateException("You have outstanding matchers, must be programming error"); 859 } 860 } 861 862 /** A pairing of a method call and an action to be performed when that call happens. */ 863 private static class StubbedCall { 864 private final MethodCall mMethodCall; 865 private final Action mAction; 866 867 public StubbedCall(MethodCall methodCall, Action action) { 868 mMethodCall = methodCall; 869 mAction = action; 870 } 871 872 @Override 873 public String toString() { 874 return "StubbedCall [methodCall=" + mMethodCall + ", action=" + mAction + "]"; 875 } 876 } 877 878 /** Represents an action to be performed as a result of a method call. */ 879 private interface Action { 880 public Object doAction(Method method, Object[] arguments) throws Throwable; 881 /** The type of the action, or null if we can't determine the type at stub time. */ 882 public Class<?> getReturnType(); 883 } 884 885 /** Represents something capable of testing if it matches an argument or not. */ 886 /*package*/ interface ArgumentMatcher { 887 public boolean matches(Object value); 888 } 889 890 /** A matcher that matches any argument. */ 891 private static class MatchAnything implements ArgumentMatcher { 892 @Override 893 public boolean matches(Object value) { return true; } 894 } 895 896 /** Encapsulates the number of times a method is called, between upper and lower bounds. */ 897 private static class CallCount { 898 private long mLowerBound; 899 private long mUpperBound; 900 901 public CallCount(long lowerBound, long upperBound) { 902 mLowerBound = lowerBound; 903 mUpperBound = upperBound; 904 } 905 906 /** Tells us if this call count matches a desired count. */ 907 public boolean matches(long total) { 908 return total >= mLowerBound && total <= mUpperBound; 909 } 910 911 /** Tells us how long we should block waiting for the verify to happen. */ 912 public long getTimeout() { 913 return 0; 914 } 915 916 public CallCount setLowerBound(long lowerBound) { 917 mLowerBound = lowerBound; 918 return this; 919 } 920 921 public CallCount setUpperBound(long upperBound) { 922 mUpperBound = upperBound; 923 return this; 924 } 925 926 @Override 927 public String toString() { 928 if (mLowerBound == mUpperBound) { 929 return "exactly " + mLowerBound + plural(" call", mLowerBound); 930 } else { 931 return "between " + mLowerBound + plural(" call", mLowerBound) + " and " + 932 mUpperBound + plural(" call", mUpperBound); 933 } 934 } 935 } 936 937 /** Encapsulates adding number of times behaviour to a call count with timeout. */ 938 public static final class Timeout extends CallCount { 939 private long mTimeoutMillis; 940 941 public Timeout(long lowerBound, long upperBound, long timeoutMillis) { 942 super(lowerBound, upperBound); 943 mTimeoutMillis = timeoutMillis; 944 } 945 946 @Override 947 public long getTimeout() { 948 return mTimeoutMillis; 949 } 950 951 public CallCount times(int times) { return setLowerBound(times).setUpperBound(times); } 952 public CallCount atLeast(long n) { return setLowerBound(n).setUpperBound(Long.MAX_VALUE); } 953 public CallCount atLeastOnce() { return setLowerBound(1).setUpperBound(Long.MAX_VALUE); } 954 public CallCount between(long n, long m) { return setLowerBound(n).setUpperBound(m); } 955 } 956 957 /** Helper method to add an 's' to a string iff the count is not 1. */ 958 private static String plural(String prefix, long count) { 959 return (count == 1) ? prefix : (prefix + "s"); 960 } 961 962 /** Helps us implement the eq(), any() and capture() and other methods on one line. */ 963 private static <T> T addMatcher(ArgumentMatcher argument, T value) { 964 sMatchArguments.add(argument); 965 return value; 966 } 967 968 /** Utility method to throw an AssertionError if an assertion fails. */ 969 private static void expect(boolean result, String message) { 970 if (!result) { 971 fail(message); 972 } 973 } 974 975 /** Throws an AssertionError exception with a message. */ 976 private static void fail(String message) { 977 throw new AssertionError(message); 978 } 979 980 /** Static mapping from class type to default value for that type. */ 981 private static final Map<Class<?>, Object> DEFAULT_RETURN_VALUE_LOOKUP; 982 static { 983 DEFAULT_RETURN_VALUE_LOOKUP = new HashMap<Class<?>, Object>(); 984 DEFAULT_RETURN_VALUE_LOOKUP.put(int.class, 0); 985 DEFAULT_RETURN_VALUE_LOOKUP.put(boolean.class, false); 986 DEFAULT_RETURN_VALUE_LOOKUP.put(byte.class, (byte) 0); 987 DEFAULT_RETURN_VALUE_LOOKUP.put(long.class, (long) 0); 988 DEFAULT_RETURN_VALUE_LOOKUP.put(short.class, (short) 0); 989 DEFAULT_RETURN_VALUE_LOOKUP.put(float.class, (float) 0); 990 DEFAULT_RETURN_VALUE_LOOKUP.put(double.class, (double) 0); 991 DEFAULT_RETURN_VALUE_LOOKUP.put(char.class, '\u0000'); 992 DEFAULT_RETURN_VALUE_LOOKUP.put(String.class, null); 993 } 994 995 /** Static lookup from primitive types to their boxed versions. */ 996 private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_BOXED_LOOKUP; 997 static { 998 PRIMITIVE_TO_BOXED_LOOKUP = new HashMap<Class<?>, Class<?>>(); 999 PRIMITIVE_TO_BOXED_LOOKUP.put(int.class, Integer.class); 1000 PRIMITIVE_TO_BOXED_LOOKUP.put(boolean.class, Boolean.class); 1001 PRIMITIVE_TO_BOXED_LOOKUP.put(byte.class, Byte.class); 1002 PRIMITIVE_TO_BOXED_LOOKUP.put(long.class, Long.class); 1003 PRIMITIVE_TO_BOXED_LOOKUP.put(short.class, Short.class); 1004 PRIMITIVE_TO_BOXED_LOOKUP.put(float.class, Float.class); 1005 PRIMITIVE_TO_BOXED_LOOKUP.put(double.class, Double.class); 1006 PRIMITIVE_TO_BOXED_LOOKUP.put(char.class, Character.class); 1007 } 1008 1009 /** For a given class type, returns the default value for that type. */ 1010 private static Object defaultReturnValue(Class<?> returnType) { 1011 return DEFAULT_RETURN_VALUE_LOOKUP.get(returnType); 1012 } 1013 1014 /** Gets a suitable class loader for use with the proxy. */ 1015 private static ClassLoader getClassLoader() { 1016 return LittleMock.class.getClassLoader(); 1017 } 1018 1019 /** Sets a member field on an object via reflection (can set private members too). */ 1020 private static void setField(Field field, Object object, Object value) throws Exception { 1021 field.setAccessible(true); 1022 field.set(object, value); 1023 field.setAccessible(false); 1024 } 1025 1026 /** Helper method to throw an IllegalStateException if given condition is not met. */ 1027 private static void checkState(boolean condition, String message) { 1028 if (!condition) { 1029 throw new IllegalStateException(message); 1030 } 1031 } 1032 1033 /** 1034 * If the input object is one of our mocks, returns the {@link DefaultInvocationHandler} 1035 * we constructed it with. Otherwise fails with {@link IllegalArgumentException}. 1036 */ 1037 private static DefaultInvocationHandler getHandlerFrom(Object mock) { 1038 try { 1039 InvocationHandler invocationHandler = Proxy.getInvocationHandler(mock); 1040 if (invocationHandler instanceof DefaultInvocationHandler) { 1041 return (DefaultInvocationHandler) invocationHandler; 1042 } 1043 } catch (IllegalArgumentException expectedIfNotAProxy) {} 1044 try { 1045 Class<?> proxyBuilder = Class.forName("com.google.dexmaker.stock.ProxyBuilder"); 1046 Method getHandlerMethod = proxyBuilder.getMethod("getInvocationHandler", Object.class); 1047 Object invocationHandler = getHandlerMethod.invoke(proxyBuilder, mock); 1048 if (invocationHandler instanceof DefaultInvocationHandler) { 1049 return (DefaultInvocationHandler) invocationHandler; 1050 } 1051 } catch (Exception expectedIfNotAProxyBuilderMock) {} 1052 throw new IllegalArgumentException("not a valid mock: " + mock); 1053 } 1054 1055 /** Create a dynamic proxy for the given class, delegating to the given invocation handler. */ 1056 private static Object createProxy(Class<?> clazz, InvocationHandler handler) { 1057 if (clazz.isInterface()) { 1058 return Proxy.newProxyInstance(getClassLoader(), new Class<?>[] { clazz }, handler); 1059 } 1060 try { 1061 Class<?> proxyBuilder = Class.forName("com.google.dexmaker.stock.ProxyBuilder"); 1062 Method forClassMethod = proxyBuilder.getMethod("forClass", Class.class); 1063 Object builder = forClassMethod.invoke(null, clazz); 1064 Method handlerMethod = builder.getClass().getMethod("handler", InvocationHandler.class); 1065 builder = handlerMethod.invoke(builder, handler); 1066 Method dexCacheMethod = builder.getClass().getMethod("dexCache", File.class); 1067 File directory = AppDataDirGuesser.getsInstance().guessSuitableDirectoryForGeneratedClasses(); 1068 builder = dexCacheMethod.invoke(builder, directory); 1069 Method buildMethod = builder.getClass().getMethod("build"); 1070 return buildMethod.invoke(builder); 1071 } catch (Exception e) { 1072 throw new IllegalStateException("Could not mock this concrete class", e); 1073 } 1074 } 1075} 1076