Первая заглушка вызывается при добавлении дополнительной заглушки

У меня есть следующий объект, который я хочу проверить:

   public class MyObject {

    @Inject
    Downloader downloader;

    public List<String> readFiles(String[] fileNames) {
        List<String> files = new LinkedList<>();
        for (String fileName : fileNames) {
            try {
                files.add(downloader.download(fileName));
            } catch (IOException e) {
                files.add("NA");
            }
        }
        return files;
    }
}

Это мой тест:

@UseModules(mockTest.MyTestModule.class)
@RunWith(JukitoRunner.class)
public class mockTest {

    @Inject Downloader downloader;
    @Inject MyObject myObject;

    private final String[] FILE_NAMES = new String[] {"fail", "fail", "testFile"};
    private final List<String> EXPECTED_FILES = Arrays.asList("NA", "NA", "mockContent");

    @Test
    public void testException() throws IOException {
        when(downloader.download(anyString()))
                .thenThrow(new IOException());

        when(downloader.download("testFile"))
                .thenReturn("mockContent");

        assertThat(myObject.readFiles(FILE_NAMES))
                .isEqualTo(EXPECTED_FILES);
    }

    public static final class MyTestModule extends TestModule {
        @Override
        protected void configureTest() {
            bindMock(Downloader.class).in(TestSingleton.class);
        }
    }
}

Я перезаписываю anyString() совпадение для конкретного аргумента. Я заглушаю download() метод, так что он возвращает значение для конкретного аргумента и в противном случае выдает IOException, который обрабатывается MyObject.readFiles,

Странная вещь здесь в том, что вторая заглушка (downloader.download("testFile")) создает исключение IOException, установленное в первой заглушке (downloader.download(anyString())). Я подтвердил это, бросив другое исключение в мою первую заглушку.

Может кто-нибудь объяснить мне, почему возникает исключение при добавлении дополнительной заглушки? Я думал, что создание заглушки не вызывает метод / другие заглушки.

3 ответа

Решение

Я думал, что создание заглушки не вызывает метод / другие заглушки.

Это предположение неверно, потому что заглушка вызывает ложные методы. Ваши методы тестирования все еще просты в Java!

С тех пор как anyString перезапишет заглушку для любой конкретной строки, вам нужно будет написать два теста или заглушку для двух конкретных аргументов:

when(downloader.download("fail")).thenThrow(new IOException());

when(downloader.download("testFile")).thenReturn("mockContent");

Mockito - очень сложный кусок кода, который старается изо всех сил, чтобы вы могли писать

when(downloader.download(anyString())).thenThrow(new IOException());

что значит " when downloader с насмешкой download метод вызывается с anyString аргумент thenThrow IOException "(То есть его можно читать слева направо).

Однако, поскольку код по-прежнему простой Java, последовательность вызовов на самом деле:

String s1 = anyString(); // 1
String s2 = downloader.download(s1); // 2
when(s2).thenThrow(new IOException()); // 3

За кулисами, Мокито должен сделать это:

  1. зарегистрировать ArgumentMatcher для любого аргумента String
  2. зарегистрировать вызов метода download на downloader макет, где аргумент определяется ранее зарегистрированным ArgumentMatcher
  3. зарегистрировать действие для ранее зарегистрированного вызова метода на макете

Если вы сейчас позвоните

 ... downloader.download("testFile") ...

downloader макет проверяет, есть ли регистр действий для "testFile" (есть, поскольку для любой строки уже есть действие) и, соответственно, IOException,

Проблема в том, что когда ты пишешь

when(downloader.download("testFile")).thenReturn("mockContent");

первое, что нужно назвать downloader.download, который вы уже оцепили, чтобы бросить исключение.

Решение состоит в том, чтобы использовать немного более универсальный синтаксис заглушки, который обеспечивает Mockito. Преимущество этого синтаксиса в том, что он не вызывает реальный метод при создании заглушки.

doThrow(IOException.class).when(downloader).download(anyString());
doReturn("mock content").when(downloader).download("test file");

Я перечислил другие преимущества этого второго синтаксиса в своем ответе здесь

Ваш второй ложный оператор переопределяется первым ложным оператором (потому что оба ложных оператора передают аргумент String). Если вы хотите рассказать о пробной версии, а также о пробном тесте, напишите 2 разных контрольных примера.

Другие вопросы по тегам