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&lt;K extends Comparable&lt;K&gt; & Cloneable&gt; extends Map&lt;K, Set&lt;Number&gt;&gt; {}
31  *
32  *     GenericsNest&lt;?&gt; 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