Мне известно о дубликате, но ответа на конкретный вопрос там не было.
Как проверить, вызывается ли метод в тестируемой системе (не макет)
У меня есть класс:
class A {
public long a() {
if (something) {
return quicklyCalculatedResult
} else {
return b() run on separate thread, with this one blocked
}
}
public long b() {} //doStuffOnCurrentThread;
}
У меня есть полный набор тестов для b (), который выполняет тяжелую работу. К сожалению, мне приходится делать уродливую мысль вроде () (устаревший код), и я не хочу копировать все тесты. Метода b (). Кроме того, оба они должны быть общедоступными.
Я хочу убедиться, что при определенных обстоятельствах a () вызывает b (), но я не могу этого сделать, потому что проверенный класс не является имитацией. Мне нужен способ проверить, что метод был вызван для реального объекта, а не только для имитации.
@JasonArmstrong Привет, это способ, но существует множество условий, которые могут привести к вызову b (). В моем реальном примере это больше похоже на «если какой-то аргумент равен нулю, дать быстрый ответ и в любом другом случае получить результат b ()». b () будет отвечать по-разному, в зависимости от заданных аргументов. Итак, чтобы проверить это с возвращенным значением вместо проверки того, был ли вызван b (), мне пришлось бы протестировать несколько сценариев, и это в основном копирование тестов b () ...
Я понимаю, о чем вы говорите, но что вы на самом деле единица измерения тестируете в этот момент a или b?
Я хочу провести модульное тестирование a (). И требование для a - «если некоторые аргументы равны нулю, тогда что-то вычислите, иначе вызовите b ()».
@JasonArmstrong. Хотя я в принципе согласен с тем, что вы говорите, бывают моменты, когда нам приходится тестировать сложный устаревший код. Для этого и было сделано частичное издевательство.
@ Дин, я слышу тебя и согласен, иногда нам приходится выбирать нежелательный путь, я просто хотел привлечь к нему внимание, чтобы, когда другие пришли посмотреть этот вопрос, они могли сделать осознанный выбор.
@JasonArmstrong. Да, вы правы.




Вы можете сделать A шпионом с помощью @Spy или Mockito.spy(). Это позволит вам вызывать и тестировать логику метода a(), но также заменяет b() инвариантом. Это можно проиллюстрировать списком:
List list = new LinkedList();
List spy = Mockito.spy(list);
// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
Спасибо! Это работает, и я проголосовал. Я дал правильный ответ @Dean, потому что его ответ был ближе к вопросу и для вызова .thenCallRealMethod (), но ваш способ тоже работает!
Mockito и другие библиотеки имитации kotlin предоставляют частичную имитацию или аналогичные функции. Вы можете указать реальные методы, которые будут вызываться, в то время как другие методы останутся заглушками:
Пример Mockito java:
A classUnderTest = mock(A.class);
when(classUnderTest.a()).thenCallRealMethod();
classUnderTest.a();
verify(classUnderTest).b()
См. Mockito Документация о частичном издевательстве. Частичное имитация не приветствуется, потому что она не соответствует хорошему дизайну ООП, но в вашем случае она соответствует своему прямому назначению, а именно тестированию сложного устаревшего кода.
Пример Kotlin с ванильным Mockito:
val classUnderTest = mock(A::class.java)
`when`(classUnderTest.a()).thenCallRealMethod()
classUnderTest.a()
verify(classUnderTest).b()
мокито-котлин предоставляет расширения, которые позволяют использовать mockito более идиоматическим способом kotlin. К сожалению, не существует способа сделать частичное издевательство идиоматическим способом kotlin, но это может быть достигнуто в mockito-kotlin следующим образом:
val classUnderTest = mock<A>()
doCallRealMethod().whenever(classUnderTest).a()
classUnderTest.a()
verify(classUnderTest).b()
MockK, идиоматическая библиотека имитации котлина, позволяет использовать эту функцию со шпионами. После создания spy класса вы можете выбрать методы-заглушки:
val classUnderTest = spyk<A>()
every { classUnderTest.b() } returns 1L
classUnderTest.a()
verify { classUnderTest.b() }
Привет спасибо! У меня были небольшие проблемы с его работой, но только из-за использования Kotlin вместо Java. Это именно то, что мне было нужно :)
ИМХО, я не думаю, что вам стоит волноваться, если Б. позвонят. Вместо этого сосредоточьтесь на условиях, которые приводят к вызову b (отрицательное значение «something» условного), а затем убедитесь, что вы получили ожидаемые результаты. Смысл насмешек в том, чтобы позволить вам упростить / контролировать вашу зависимость / привязку к классам, которые не тестируются. Кроме того, вам следует, даже если вы не хотите, перенести тесты так, чтобы они сосредоточились на b, но должны сосредоточиться на b.