Проверьте входные параметры с помощью groovy.mock.interceptor - PullRequest
0 голосов
/ 19 ноября 2018

Я пытаюсь провести несколько юнит-тестов, используя groovy.mock.interceptor.Я хочу утверждать, что функция действительно вызывалась с некоторыми конкретными значениями в качестве аргументов.Я не могу найти, как это сделать.Любая помощь?

Вот как это выглядит:

import groovy.mock.interceptor.MockFor
import org.junit.Test

class MyClassTest extends GroovyTestCase {
    @Test
    void test_correctness_of_passed_arguments() {
        def mock = new MockFor(MyClass)
        mock.ignore('main')
        mock.demand.myFunction{a, b, c -> '0'} // Is this where I should enforce the input params?
        mock.use {
            def foo = new MyClass()
            foo.main()  // <--- this is in there that it gets executed
        }
        mock.expect.verify()
        mock.demand.recorded[0] // <--- can I get what has been passed afterwards?
    }
}

Ответы [ 2 ]

0 голосов
/ 19 ноября 2018

Хорошо, это выполнимо, поскольку mock.demand.myFunction принимает нормальное Closure.В итоге получилось что-то вроде этого:

import groovy.mock.interceptor.MockFor
import org.junit.Test

class MyClassTest extends GroovyTestCase {
    @Test
    void test_correctness_of_passed_arguments() {
        def mock = new MockFor(MyClass)
        mock.ignore('main')
        def res = []
        // the mocked function stores its values in `res` and returns '0'
        mock.demand.myFunction(4) {a, b, c ->
            res.add([a, b, c])
            '0'
        }
        mock.use {
            def foo = new MyClass()
            foo.main()  // <--- this is in there that it gets executed
        }
        mock.expect.verify()
        res[0] // <--- I can then access the values there
    }
}

В приведенном выше примере я прошу myFunction быть вызванным 4 раз.

0 голосов
/ 19 ноября 2018

Вы не можете достичь ожидаемого поведения с 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 зависимость вместе со Споком.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...