1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.stubbing.defaultanswers; 6 7 import org.mockito.MockSettings; 8 import org.mockito.internal.InternalMockHandler; 9 import org.mockito.internal.MockitoCore; 10 import org.mockito.internal.creation.settings.CreationSettings; 11 import org.mockito.internal.stubbing.InvocationContainerImpl; 12 import org.mockito.internal.stubbing.StubbedInvocationMatcher; 13 import org.mockito.internal.util.MockUtil; 14 import org.mockito.internal.util.reflection.GenericMetadataSupport; 15 import org.mockito.invocation.InvocationOnMock; 16 import org.mockito.stubbing.Answer; 17 18 import java.io.Serializable; 19 20 import static org.mockito.Mockito.withSettings; 21 22 /** 23 * Returning deep stub implementation. 24 * 25 * Will return previously created mock if the invocation matches. 26 * 27 * <p>Supports nested generic information, with this answer you can write code like this : 28 * 29 * <pre class="code"><code class="java"> 30 * interface GenericsNest<K extends Comparable<K> & Cloneable> extends Map<K, Set<Number>> {} 31 * 32 * GenericsNest<?> mock = mock(GenericsNest.class, new ReturnsGenericDeepStubs()); 33 * Number number = mock.entrySet().iterator().next().getValue().iterator().next(); 34 * </code></pre> 35 * </p> 36 * 37 * @see org.mockito.Mockito#RETURNS_DEEP_STUBS 38 * @see org.mockito.Answers#RETURNS_DEEP_STUBS 39 */ 40 public class ReturnsDeepStubs implements Answer<Object>, Serializable { 41 42 private static final long serialVersionUID = -7105341425736035847L; 43 44 private MockitoCore mockitoCore = new MockitoCore(); 45 private ReturnsEmptyValues delegate = new ReturnsEmptyValues(); 46 answer(InvocationOnMock invocation)47 public Object answer(InvocationOnMock invocation) throws Throwable { 48 GenericMetadataSupport returnTypeGenericMetadata = 49 actualParameterizedType(invocation.getMock()).resolveGenericReturnType(invocation.getMethod()); 50 51 Class<?> rawType = returnTypeGenericMetadata.rawType(); 52 if (!mockitoCore.isTypeMockable(rawType)) { 53 return delegate.returnValueFor(rawType); 54 } 55 56 return getMock(invocation, returnTypeGenericMetadata); 57 } 58 getMock(InvocationOnMock invocation, GenericMetadataSupport returnTypeGenericMetadata)59 private Object getMock(InvocationOnMock invocation, GenericMetadataSupport returnTypeGenericMetadata) throws Throwable { 60 InternalMockHandler<Object> handler = new MockUtil().getMockHandler(invocation.getMock()); 61 InvocationContainerImpl container = (InvocationContainerImpl) handler.getInvocationContainer(); 62 63 // matches invocation for verification 64 for (StubbedInvocationMatcher stubbedInvocationMatcher : container.getStubbedInvocations()) { 65 if(container.getInvocationForStubbing().matches(stubbedInvocationMatcher.getInvocation())) { 66 return stubbedInvocationMatcher.answer(invocation); 67 } 68 } 69 70 // deep stub 71 return recordDeepStubMock(createNewDeepStubMock(returnTypeGenericMetadata), container); 72 } 73 74 /** 75 * Creates a mock using the Generics Metadata. 76 * 77 * <li>Finally as we want to mock the actual type, but we want to pass along the contextual generics meta-data 78 * that was resolved for the current return type, for this to happen we associate to the mock an new instance of 79 * {@link ReturnsDeepStubs} answer in which we will store the returned type generic metadata. 80 * 81 * @param returnTypeGenericMetadata The metadata to use to create the new mock. 82 * @return The mock 83 */ createNewDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata)84 private Object createNewDeepStubMock(GenericMetadataSupport returnTypeGenericMetadata) { 85 return mockitoCore.mock( 86 returnTypeGenericMetadata.rawType(), 87 withSettingsUsing(returnTypeGenericMetadata) 88 ); 89 } 90 withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata)91 private MockSettings withSettingsUsing(GenericMetadataSupport returnTypeGenericMetadata) { 92 MockSettings mockSettings = 93 returnTypeGenericMetadata.rawExtraInterfaces().length > 0 ? 94 withSettings().extraInterfaces(returnTypeGenericMetadata.rawExtraInterfaces()) 95 : withSettings(); 96 97 return mockSettings 98 .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)); 99 } 100 returnsDeepStubsAnswerUsing(final GenericMetadataSupport returnTypeGenericMetadata)101 private ReturnsDeepStubs returnsDeepStubsAnswerUsing(final GenericMetadataSupport returnTypeGenericMetadata) { 102 return new ReturnsDeepStubs() { 103 @Override 104 protected GenericMetadataSupport actualParameterizedType(Object mock) { 105 return returnTypeGenericMetadata; 106 } 107 }; 108 } 109 110 private Object recordDeepStubMock(final Object mock, InvocationContainerImpl container) throws Throwable { 111 112 container.addAnswer(new Answer<Object>() { 113 public Object answer(InvocationOnMock invocation) throws Throwable { 114 return mock; 115 } 116 }, false); 117 118 return mock; 119 } 120 121 protected GenericMetadataSupport actualParameterizedType(Object mock) { 122 CreationSettings mockSettings = (CreationSettings) new MockUtil().getMockHandler(mock).getMockSettings(); 123 return GenericMetadataSupport.inferFrom(mockSettings.getTypeToMock()); 124 } 125 } 126