15
15
import java .util .Arrays ;
16
16
import java .util .Collection ;
17
17
import java .util .List ;
18
+ import java .util .stream .Stream ;
18
19
19
20
import org .mockito .internal .util .MockUtil ;
20
21
@@ -28,26 +29,38 @@ public TypeBasedCandidateFilter(MockCandidateFilter next) {
28
29
29
30
protected boolean isCompatibleTypes (Type typeToMock , Type mockType , Field injectMocksField ) {
30
31
boolean result = false ;
31
- if (typeToMock instanceof ParameterizedType && mockType instanceof ParameterizedType ) {
32
- // ParameterizedType.equals() is documented as:
33
- // "Instances of classes that implement this interface must implement
34
- // an equals() method that equates any two instances that share the
35
- // same generic type declaration and have equal type parameters."
36
- // Unfortunately, e.g. Wildcard parameter "?" doesn't equal java.lang.String,
37
- // and e.g. Set doesn't equal TreeSet, so roll our own comparison if
38
- // ParameterizedTypeImpl.equals() returns false
39
- if (typeToMock .equals (mockType )) {
40
- result = true ;
32
+ if (typeToMock instanceof ParameterizedType ) {
33
+ if (mockType instanceof ParameterizedType ) {
34
+ // ParameterizedType.equals() is documented as:
35
+ // "Instances of classes that implement this interface must implement
36
+ // an equals() method that equates any two instances that share the
37
+ // same generic type declaration and have equal type parameters."
38
+ // Unfortunately, e.g. Wildcard parameter "?" doesn't equal java.lang.String,
39
+ // and e.g. Set doesn't equal TreeSet, so roll our own comparison if
40
+ // ParameterizedTypeImpl.equals() returns false
41
+ if (typeToMock .equals (mockType )) {
42
+ result = true ;
43
+ } else {
44
+ ParameterizedType genericTypeToMock = (ParameterizedType ) typeToMock ;
45
+ ParameterizedType genericMockType = (ParameterizedType ) mockType ;
46
+ Type [] actualTypeArguments = genericTypeToMock .getActualTypeArguments ();
47
+ Type [] actualTypeArguments2 = genericMockType .getActualTypeArguments ();
48
+ // Recurse on type parameters, so we properly test whether e.g. Wildcard bounds
49
+ // have a match
50
+ result =
51
+ recurseOnTypeArguments (
52
+ injectMocksField , actualTypeArguments , actualTypeArguments2 );
53
+ }
41
54
} else {
42
- ParameterizedType genericTypeToMock = (ParameterizedType ) typeToMock ;
43
- ParameterizedType genericMockType = (ParameterizedType ) mockType ;
44
- Type [] actualTypeArguments = genericTypeToMock .getActualTypeArguments ();
45
- Type [] actualTypeArguments2 = genericMockType .getActualTypeArguments ();
46
- // Recurse on type parameters, so we properly test whether e.g. Wildcard bounds
47
- // have a match
55
+ // mockType is a non-parameterized Class, i.e. a concrete class.
56
+ // so walk concrete class' type hierarchy
57
+ Class <?> concreteMockClass = (Class <?>) mockType ;
58
+ Stream <Type > mockSuperTypes = getSuperTypes (concreteMockClass );
48
59
result =
49
- recurseOnTypeArguments (
50
- injectMocksField , actualTypeArguments , actualTypeArguments2 );
60
+ mockSuperTypes .anyMatch (
61
+ mockSuperType ->
62
+ isCompatibleTypes (
63
+ typeToMock , mockSuperType , injectMocksField ));
51
64
}
52
65
} else if (typeToMock instanceof WildcardType ) {
53
66
WildcardType wildcardTypeToMock = (WildcardType ) typeToMock ;
@@ -56,12 +69,19 @@ protected boolean isCompatibleTypes(Type typeToMock, Type mockType, Field inject
56
69
Arrays .stream (upperBounds )
57
70
.anyMatch (t -> isCompatibleTypes (t , mockType , injectMocksField ));
58
71
} else if (typeToMock instanceof Class && mockType instanceof Class ) {
59
- result = ((Class ) typeToMock ).isAssignableFrom ((Class ) mockType );
72
+ result = ((Class <?> ) typeToMock ).isAssignableFrom ((Class <?> ) mockType );
60
73
} // no need to check for GenericArrayType, as Mockito cannot mock this anyway
61
74
62
75
return result ;
63
76
}
64
77
78
+ private Stream <Type > getSuperTypes (Class <?> concreteMockClass ) {
79
+ Stream <Type > mockInterfaces = Arrays .stream (concreteMockClass .getGenericInterfaces ());
80
+ Stream <Type > mockSuperTypes =
81
+ Stream .concat (mockInterfaces , Stream .of (concreteMockClass .getGenericSuperclass ()));
82
+ return mockSuperTypes ;
83
+ }
84
+
65
85
private boolean recurseOnTypeArguments (
66
86
Field injectMocksField , Type [] actualTypeArguments , Type [] actualTypeArguments2 ) {
67
87
boolean isCompatible = true ;
@@ -76,30 +96,44 @@ private boolean recurseOnTypeArguments(
76
96
// The TypeVariable`s actual type is declared by the field containing
77
97
// the object under test, i.e. the field annotated with @InjectMocks
78
98
// e.g. @InjectMocks ClassUnderTest<String, Integer> underTest = ..
79
- Type [] injectMocksFieldTypeParameters =
80
- ((ParameterizedType ) injectMocksField .getGenericType ())
81
- .getActualTypeArguments ();
82
- // Find index of given TypeVariable where it was defined, e.g. 0 for T1 in
83
- // ClassUnderTest<T1, T2>
84
- // (we're always able to find it, otherwise test class wouldn't have compiled))
85
- TypeVariable <?>[] genericTypeParameters =
86
- injectMocksField .getType ().getTypeParameters ();
87
- int variableIndex = -1 ;
88
- for (int i2 = 0 ; i2 < genericTypeParameters .length ; i2 ++) {
89
- if (genericTypeParameters [i2 ].equals (typeVariable )) {
90
- variableIndex = i2 ;
91
- break ;
99
+
100
+ Type genericType = injectMocksField .getGenericType ();
101
+ if (genericType instanceof ParameterizedType ) {
102
+ Type [] injectMocksFieldTypeParameters =
103
+ ((ParameterizedType ) genericType ).getActualTypeArguments ();
104
+ // Find index of given TypeVariable where it was defined, e.g. 0 for T1 in
105
+ // ClassUnderTest<T1, T2>
106
+ // (we're always able to find it, otherwise test class wouldn't have compiled))
107
+ TypeVariable <?>[] genericTypeParameters =
108
+ injectMocksField .getType ().getTypeParameters ();
109
+ int variableIndex = -1 ;
110
+ for (int i2 = 0 ; i2 < genericTypeParameters .length ; i2 ++) {
111
+ if (genericTypeParameters [i2 ].equals (typeVariable )) {
112
+ variableIndex = i2 ;
113
+ break ;
114
+ }
92
115
}
116
+ // now test whether actual type for the type variable is compatible, e.g. for
117
+ // class ClassUnderTest<T1, T2> {..}
118
+ // T1 would be the String in
119
+ // ClassUnderTest<String, Integer> underTest = ..
120
+ isCompatible &=
121
+ isCompatibleTypes (
122
+ injectMocksFieldTypeParameters [variableIndex ],
123
+ actualTypeArgument2 ,
124
+ injectMocksField );
125
+ } else {
126
+ // must be a concrete class, recurse on super types that may have type
127
+ // parameters
128
+ isCompatible &=
129
+ getSuperTypes ((Class <?>) genericType )
130
+ .anyMatch (
131
+ superType ->
132
+ isCompatibleTypes (
133
+ superType ,
134
+ actualTypeArgument2 ,
135
+ injectMocksField ));
93
136
}
94
- // now test whether actual type for the type variable is compatible, e.g. for
95
- // class ClassUnderTest<T1, T2> {..}
96
- // T1 would be the String in
97
- // ClassUnderTest<String, Integer> underTest = ..
98
- isCompatible &=
99
- isCompatibleTypes (
100
- injectMocksFieldTypeParameters [variableIndex ],
101
- actualTypeArgument2 ,
102
- injectMocksField );
103
137
} else {
104
138
isCompatible &=
105
139
isCompatibleTypes (
@@ -119,12 +153,12 @@ public OngoingInjector filterCandidate(
119
153
List <Object > mockTypeMatches = new ArrayList <>();
120
154
for (Object mock : mocks ) {
121
155
if (candidateFieldToBeInjected .getType ().isAssignableFrom (mock .getClass ())) {
122
- Type genericMockType = MockUtil .getMockSettings (mock ).getGenericTypeToMock ();
123
- Type genericType = candidateFieldToBeInjected .getGenericType ();
124
- boolean bothHaveGenericTypeInfo = genericType != null && genericMockType != null ;
125
- if (bothHaveGenericTypeInfo ) {
156
+ Type mockType = MockUtil .getMockSettings (mock ).getGenericTypeToMock ();
157
+ Type typeToMock = candidateFieldToBeInjected .getGenericType ();
158
+ boolean bothHaveTypeInfo = typeToMock != null && mockType != null ;
159
+ if (bothHaveTypeInfo ) {
126
160
// be more specific if generic type information is available
127
- if (isCompatibleTypes (genericType , genericMockType , injectMocksField )) {
161
+ if (isCompatibleTypes (typeToMock , mockType , injectMocksField )) {
128
162
mockTypeMatches .add (mock );
129
163
} // else filter out mock, as generic types don't match
130
164
} else {
0 commit comments