Вы не можете достичь ожидаемого поведения с MockFor
классом.Игнорирование метода main
имеет один существенный эффект - внутренний метод myFunction
выполняется, но происходит без присутствия MockInterceptor
.Вы можете поместить точку останова в класс groovy.mock.MockProxyMetaClass
в начале invokeMethod
(строка 74) и запустить отладчик, чтобы увидеть, что происходит.
public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
if (null == interceptor && !fallingThrough) {
throw new RuntimeException("cannot invoke method '" + methodName + "' without interceptor");
}
Object result = FALL_THROUGH_MARKER;
if (interceptor != null) {
result = interceptor.beforeInvoke(object, methodName, arguments);
}
if (result == FALL_THROUGH_MARKER) {
Interceptor saved = interceptor;
interceptor = null;
boolean savedFallingThrough = fallingThrough;
fallingThrough = true;
result = adaptee.invokeMethod(object, methodName, arguments);
fallingThrough = savedFallingThrough;
interceptor = saved;
}
return result;
}
Вызов метода foo.main()
в блоке mock.use {}
вызывает этот метод для ненулевого перехватчика.Результат, возвращаемый interceptor.beforeInvoke()
, равен FALL_THROUGH_MARKER
, поскольку метод main
помечен как проигнорированный.В этом случае перехватчик временно устанавливается на null
, и метод вызывается обычным способом - он вызывает внутренний метод myFunction
, но этот факт не записывается из-за перехватчика null
в этой точке.
По сути, в вашем тестовом примере вы рассматриваете фиктивный объект не как фиктивный, а скорее как шпионский объект.Стандартная библиотека макетов Groovy не поддерживает шпионские объекты, но вы можете использовать, например, Spock Framework для написания тестов с использованием шпионских объектов.Тест, который вы показали в этом вопросе, может выглядеть следующим образом с помощью Spock:
import spock.lang.Specification
class ExampleSpec extends Specification {
static class MyClass {
def main() {
return myFunction(0, 0 ,0)
}
def myFunction(def a, def b, def c) {
return '2'
}
}
def "should call myFunction with specific parameters"() {
given:
def foo = Spy(MyClass)
when:
foo.main()
then:
1 * foo.myFunction(0, 0, 0)
and:
0 * foo.myFunction(1,0,0)
}
}
Он выполняет настоящий метод foo.main()
, но он проверяет метод foo.myFunction()
и записывает вызовы и тесты, если метод был вызванс правильными параметрами - он записывает, что он был вызван один раз с параметрами (0, 0, 0)
и что он не был вызван с параметрами (1, 0, 0)
.
ВАЖНО: Если вы создаете ложные / шпионские объекты изклассы, а не интерфейсы, тогда вам нужно добавить cglib-nodep
зависимость вместе со Споком.