У меня есть несколько очень похожих проектов, все Java, SpringBoot и Maven. Все они имеют один класс с одинаковым именем и почти одинаковым содержимым. Я добавил дополнительный метод в один из них, класс с проблемой, которую я собираюсь описать, но я почти уверен, что детали - это совпадение.
Каждый проект также имеет тестовый класс, соответствующий этому очень похожий класс, и скелет этого тестового класса идентичен в каждом классе. Тестовый класс имеет @InjectMocks
для тестируемого класса (CUT) и две аннотации @Mock
, одна из которых соответствует переменной экземпляра CUT.
Тестовый класс имеет @Before
метод, который создает переменную экземпляра, используемую тестами.
Все вариации класса теста имеют "@RunWith(MockitoJUnitRunner.class)
".
Если я запускаю один из "хороших" тестов и задаю точка останова в первой строке метода @Before, а затем просмотр переменных «this» на панели переменных. Я вижу типы двух переменных экземпляра @ Mock-ed, заканчивающихся на «$ MockitoMock».
Если я делаю то же самое в «плохом» тесте, типы двух переменных @ Mock-ed НЕ заканчиваются на «$MockitoMock
». Фактически, это нормальные экземпляры соответствующих классов, а не фиктивных классов.
Еще более любопытно, что в «плохом» тесте я пытался делать явные вызовы «instvar = mock(clazz.class)
» в @Before
метод, и после того, как я перешагну через них, тип переменной экземпляра будет STILL, а не фиктивный тип, ОДНАКО, когда я нажимаю на переменную экземпляра, на панели toString отображается «Mock for ..., hashCode: 1028811481
». Если я «Возобновлю» в этот момент, я могу достичь точки останова в якобы имитируемом классе с тем же экземпляром, значение toString которого говорит «Mock for ...
».
Это проблема словами. Теперь, я думаю, я покажу некоторый код.
Вот часть "плохого" класса теста:
@RunWith(MockitoJUnitRunner.class)
public class RestClientTest {
@InjectMocks
RestClient restClient;
@Mock
RestClientFactory restClientFactory;
@Mock
RestTemplate restTemplate;
HttpEntity<String> requestEntity;
@Before
public void setup() {
requestEntity = new HttpEntity<>(new HttpHeaders());
restClientFactory = mock(RestClientFactory.class);
restTemplate = mock(RestTemplate.class);
ReflectionTestUtils.setField(restClient, "restClientFactory", restClientFactory);
}
Вот часть "хорошего" класса теста:
@RunWith(MockitoJUnitRunner.class)
public class RestClientTest {
@InjectMocks
RestClient restClient;
@Mock
RestClientFactory restClientFactory;
@Mock
RestTemplate restTemplate;
HttpEntity<String> requestEntity;
@Before
public void setup() {
requestEntity = new HttpEntity<>(new HttpHeaders());
}
Я определил, что как "хорошие", так и "плохие" проекты используют версию 2.15.0 mockito-core.
Обновление :
I попытался войти в «ложный» вызов в плохом тесте и установить там точки останова, потому что он идет здесь от обработки аннотации, чтобы я мог видеть поведение как плохого, так и хорошего случая.
Вот что я видел в хорошем случае:
Я перешел к строке 65 и вошел в «createMock ()». Это поместило меня в класс MockUtil:
Тип «mockMaker» - «org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker».
Я перешел к строке 35 и перешел к методу mockMaker.createMock ():
Теперь давайте начнем сначала и запустите «плохой» случай:
Сначала мы достигли начальной точки останова:
А затем здесь:
Теперь мы видим, что тип «mockMaker» отличается от хорошего случая. Тип "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker".
Я не собираюсь продолжать проходить через это, но этот путь действительно производит "поддельный макет" с другим значением toString .
Теперь, когда я думаю об этом, этот экземпляр похож на «шпиона» в том смысле, что им управляет Mockito, но по умолчанию все методы вызывают исходный метод класса. Я понятия не имею, почему здесь идет другой путь.
Я надеюсь, что этой информации будет достаточно, чтобы дать подсказку тому, кто лучше понимает, как это работает.