Use generic type information in TypeBasedCandidateFilter to circumvent type erasure
jfrantzius opened this issue · 10 comments
Hi,
when using @InjectMocks
in combination with multiple @Mock
fields that have the same generic type (after type erasure), this fails unless the field names in injectee and test class are equal.
I stumbled over this when I added a second Provider<T>
field to both test and injectee class, and I guess most other people doing the same will also fail here as miserably as I did (also because googling for @InjectMock
doesn't show the relevant InjectMocks documentation in the first result page ...)
E.g. this test will fail because I named the test class @Mock
fields with the suffix mock
. When deleting that name suffix, the test succeeds.
import static org.junit.jupiter.api.Assertions.assertNotNull;
import javax.inject.Provider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class InjectMockTest {
public static class UnderTest {
Provider<String> stringProvider;
Provider<Integer> intProvider;
}
@Mock
private Provider<String> stringProviderMock;
@Mock
private Provider<Integer> intProviderMock;
@InjectMocks
private UnderTest underTest;
@Test
public void testInjectMock() {
assertNotNull(underTest.stringProvider);
assertNotNull(underTest.intProvider);
}
}
I saw in the debugger that in TypeBasedCandidateFilter.filterCandidate()
, candidateFieldToBeInjected.getGenericType().getTypeName()
yields e.g. "javax.inject.Provider<java.lang.Integer>"
. So if Mockito would maintain the @Mock
field's java.lang.reflect.Field
along with the mock object, the generic type names could be compared.
This would effectively solve the problem of type erasure for this usecase, which I'd guess is quite common ...
Duplicate of #1066
Hi @TimvdLippe , my suggestion is not about using the name parameter of the @Mock
annotation, but using Field
type information to perform the match.
So with my suggestion, there would be no need to use the above at all or having to align field names in the first place.
@TimvdLippe please find PR #2923 to prove the point
Hi @TimvdLippe , I saw this in https://github.com/mockito/mockito/actions/runs/4244476122/jobs/7425248431 :
[Undefined reference | android-api-level-26-8.0.0_r2] org.mockito.internal.configuration.injection.filter.(TypeBasedCandidateFilter.java:36)
>> String java.lang.reflect.Type.getTypeName()
Does Mockito want to maintain support down to Android API level 26?
The method in question was added in Android API level 28: https://developer.android.com/reference/java/lang/reflect/Type#getTypeName()
Yes we need to maintain support for level 26 for our Android userbase.
Please have a look @TimvdLippe
I fixed formatting, could you please approve workflow @TimvdLippe ?
@TimvdLippe it now also works for Spys and supports matching things like TreeSet<String>
to Set<?>
@TimvdLippe spotless check shouldn't fail anymore now, could you please start the workflow again?
@TimvdLippe PR build #2923 was successful, is there anything that you'd miss in the PR?