Подсчет косвенных вызовов методов Mockito

У меня проблемы с подсчетом вызовов методов с Mockito. Проблема в том, что метод, который я хочу подсчитать, вызывается в тестовом классе косвенно другим методом. Вот код:

public class ClassForTest {
    private Integer value;

    public void doSmth() {
        prepareValue("First call");
        prepareValue("Second call");
        prepareValue("Third call");
        System.out.println(value);
    }

    protected void prepareValue(String msg) {
        System.out.println("This is message: " + msg);
        value++;
    }
}

И тестовый класс:

public class ClassForTestTest extends TestCase {
    @Test
    public void testDoSmth() {
        ClassForTest testMock = mock(ClassForTest.class);
        doNothing().when(testMock).prepareValue(anyString());
        testMock.doSmth();
        verify(testMock, times(3)).prepareValue(anyString());
    }
}

Имея такое исключение:

Wanted but not invoked:
classForTest.prepareValue(<any>);
-> at org.testing.ClassForTestTest.testDoSmth(ClassForTestTest.java:24)

However, there were other interactions with this mock:
-> at org.testing.ClassForTestTest.testDoSmth(ClassForTestTest.java:21)

Любые идеи, пожалуйста. Заранее спасибо!


person catdog    schedule 20.10.2011    source источник


Ответы (4)


Это сработает. Использование spy вызывает базовый метод. Убедитесь, что value инициализирован первым.

    @Test
    public void testDoSmth() {
        ClassForTest testMock = spy(new ClassForTest());
        testMock.doSmth();
        verify(testMock, times(3)).prepareValue(anyString());
    }

    public class ClassForTest {
        private Integer value = 0;

        public void doSmth() {
            prepareValue("First call");
            prepareValue("Second call");
            prepareValue("Third call");
            System.out.println(value);
        }

        protected void prepareValue(String msg) {
            System.out.println("This is message: " + msg);
            value++;
        }
    }
person Garrett Hall    schedule 20.10.2011
comment
Спасибо! Это сработало. Я пробовал шпионить, но неправильно, как я вижу. - person catdog; 20.10.2011

Это указывает на то, что вам нужен рефакторинг для улучшения вашего дизайна. Один класс должен быть полностью тестируемым, без необходимости макетирования его частей. Все части, которые, по вашему мнению, нужно смоделировать, должны быть извлечены в один или несколько совместных объектов. Не попадайтесь в ловушку частичной имитации. Послушайте, что говорят вам тесты. Ваше будущее «я» скажет вам спасибо.

person Ryan Stewart    schedule 20.10.2011
comment
Да, я понимаю. Но в реальном приложении метод prepareValue вызывает хранимую процедуру оракула, поэтому я не могу проверить ее на базе данных в памяти. Так что я просто пытаюсь подсчитать количество звонков. - person catdog; 20.10.2011
comment
Я согласен, но иногда вы имеете дело с устаревшим, сторонним кодом или выполняете сложный интеграционный тест, и вам нужно использовать шпиона. - person Garrett Hall; 20.10.2011
comment
@catdog: То, что делает prepareValue(), не имеет отношения к тому факту, что тесты ведут вас в определенном направлении, но тот факт, что он вызывает хранимую процедуру, почти доказывает мою точку зрения, что ее следует передать соавтору. Это не то, что следует смешивать с вашей логикой таким образом. - person Ryan Stewart; 20.10.2011
comment
@Gnon: Это, безусловно, правда, но нет никаких указаний на то, что здесь это так. - person Ryan Stewart; 20.10.2011
comment
@catdog Я добавил еще один ответ, если вам интересно, как реорганизовать делегата, над которым вы можете издеваться. - person Garrett Hall; 20.10.2011
comment
@RyanStewart Но если это действительно так, ваше утверждение не является общим. - person Yngve Sneen Lindal; 09.10.2014

Вы издеваетесь над тестируемым классом. Насмешки предназначены для зависимостей тестируемого класса, а не самого класса.

Я подозреваю, что вы хотите Mockito.spy(). Тем не менее, это частичная насмешка, против которой Mockito Javadoc не рекомендует.

person Bringer128    schedule 20.10.2011

В качестве альтернативы, если вы хотите провести рефакторинг для тестируемости, вы можете сделать следующее:

@Test
public void testDoSmth() {
    Preparer preparer = mock(Preparer.class);
    ClassForTest cft = new ClassForTest(preparer);
    cft.doSmth();
    verify(preparer, times(3)).prepareValue(anyString());
}

public class ClassForTest {
    private final Preparer preparer;

    public ClassForTest(Preparer preparer) {
        this.preparer = preparer;
    }

    public void doSmth() {
        preparer.prepareValue("First call");
        preparer.prepareValue("Second call");
        preparer.prepareValue("Third call");
        System.out.println(preparer.getValue());
    }
}

public class Preparer {
    private Integer value = 0;

    public void prepareValue(String msg) {
        System.out.println("This is message: " + msg);
        value++;
    }

    public Integer getValue() {
        return value;
    }
}
person Garrett Hall    schedule 20.10.2011
comment
Это вполне разумный рефакторинг. Но это вопрос, что лучше создать много Preparers или использовать частичное mocking. - person catdog; 25.10.2011