Mockito: как заглушить методы void для запуска некоторого кода при вызове

Я хочу заглушить класс репозитория, чтобы протестировать другой класс (класс держателя), у которого есть репозиторий. Интерфейс репозитория поддерживает операции CRUD и имеет много методов, но мой модульный тест для класса Holder должен вызывать только два из них. Интерфейс репозитория:

public interface IRepo {
    public void remove(String... sarr);

    public void add(String... sarr);

    //Lots of other methods I don't need now
}

Я хочу создать макет репозитория, который может хранить экземпляры, определять логику только для add и remove, а также предоставлять средства проверки того, что в нем хранится после вызова добавления и удаления.

If I do:

IRepo repoMock = mock(IRepo.class);

Затем у меня есть тупой объект, который ничего не делает для каждого метода. Все в порядке, теперь мне просто нужно определить поведение для добавления и удаления.

Я мог бы создать Set<String> и заглушить только эти два метода для работы с набором. Затем я бы создал экземпляр держателя, у которого есть IRepo, внедрил частично заглушенный макет и после тренировки держателя проверил набор, чтобы убедиться, что он содержит то, что должен.

Мне удалось частично заглушить метод void, такой как remove, с помощью устаревшего метода stubVoid:

Set<String> mySet = new HashSet<>();
stubVoid(repoMock).toAnswer(new Answer<Void>() {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        String[] stringsToDelete = (String[]) args[0];
        mySet.removeAll(Arrays.asList(stringsToDelete));
        return null;
    }
}).on().remove(Matchers.<String>anyVararg());

Но устарел, и это не намного лучше, чем создание частичной реализации для IRepo. Есть ли способ лучше?

ПРИМЕЧАНИЕ. Пожалуйста, ответьте только на Java 7, это должно работать на Android.


person Mister Smith    schedule 21.08.2015    source источник


Ответы (3)


Вы можете использовать

  Mockito.doAnswer(new Answer<Void>() {
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            //DO SOMETHING
            return null;
        }
    }).when(...).remove(Matchers.<String>anyVararg());

Из Javadoc:

Используйте doAnswer(), если вы хотите заглушить метод void с помощью универсального ответа.

Заглушка пустот требует другого подхода, чем Mockito.when(Object), потому что компилятору не нравятся методы void внутри скобок...

Пример:

    doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Mock mock = invocation.getMock();
        return null;
    }}).when(mock).someMethod();

См. примеры в javadoc для Mockito.

person Jens    schedule 21.08.2015
comment
Интересно, что нет doSomething принятия Runnable вместо того, чтобы взломать пустой ответ. Является ли мой вариант использования настолько далеким, что он не заслуживает того, чтобы быть в API Mockito? - person Mister Smith; 21.08.2015
comment
Я сомневаюсь, что есть какая-либо библиотека, удовлетворяющая всех пользователей. Mockito очень легко расширить (см. мой ответ) практически для любых целей. Кстати, Runnable не будет работать в вашем примере, так как вам нужны аргументы вызова. - person Ruben; 22.08.2015
comment
Исправлена ​​моя проблема с попыткой принудительно изменить некоторые данные в середине теста, спасибо! - person EM-Creations; 09.11.2017

Предположим, у вас есть

public class IFace {
    public void yourMethod() {            
    }
}

Тогда для издевательства над ним вам нужно

    IFace mock = Mockito.mock(IFace.class);
    Mockito.doAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            //PUT YOUR CODE HERE
            return null;
        }
    }).when(mock).yourMethod();
person talex    schedule 21.08.2015

Если вам действительно не нравится return null в вашей реализации Answer, вы можете создать свою собственную реализацию ответа, которая делегирует методу void:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation);
        return null;
    }

    protected abstract void doSomething(InvocationOnMock invocation);    
}

Тогда в вашем тесте на одну строку меньше:

    Set<String> mySet = new HashSet<>();
    Mockito.doAnswer(new DoesSomethingAnswer() {
        @Override
        protected void doSomething(InvocationOnMock invocation) {
            Object[] args = invocation.getArguments();
            String[] stringsToDelete = (String[]) args[0];
            mySet.removeAll(Arrays.asList(stringsToDelete));
        }
    }).when(repoMock).remove(Matchers.<String> anyVararg());

Или, если вам просто нужны аргументы от вызова:

public abstract class DoesSomethingAnswer implements Answer<Void> {

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        doSomething(invocation.getArguments());
        return null;
    }

    protected abstract void doSomething(Object[] args);
}
person Ruben    schedule 22.08.2015