Как сделать модульный тест на Исключения?

Как Вы знаете, исключение выдается при условии аварийных сценариев. Таким образом, как к аналогу эти исключения? Я чувствую, что это - проблема. Для таких фрагментов кода:

public String getServerName() {
    try {

        InetAddress addr = InetAddress.getLocalHost();
        String hostname = addr.getHostName();
        return hostname;
    }
    catch (Exception e) {
        e.printStackTrace();
        return "";
    }
}

У кого-либо есть хорошие идеи?

33
задан 4 revs, 3 users 84% 21 November 2011 в 20:11
поделиться

4 ответа

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

Возьмите ваш код в качестве примера. Было бы очень сложно заставить ваш getServerName () внутренне генерировать исключение в контексте простого модульного теста. Проблема в том, что для возникновения исключения код (обычно) должен быть запущен на машине, чья сеть нарушена. Организовать это в модульном тесте, вероятно, невозможно ... вам придется намеренно неправильно настроить машину перед запуском теста.

Итак, каков ответ?

  1. В некоторых случаях простой ответ - просто принять прагматичное решение, а не стремиться к полному тестированию. Ваш метод - хороший тому пример. Из проверки кода должно быть ясно, что на самом деле делает метод. Его тестирование ничего не докажет (кроме см. Ниже ** ). Все, что вы делаете, - это увеличиваете количество тестов и количество тестовых покрытий, ни одна из которых не должна входить в проект целей .

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

Вот ваш пример этого «лечения». (Это немного надумано ...)

public interface ILocalDetails {
    InetAddress getLocalHost() throws UnknownHostException;
    ...
}

public class LocalDetails implements ILocalDetails {
    public InetAddress getLocalHost() throws UnknownHostException {
        return InetAddress.getLocalHost();
    }
}

public class SomeClass {
    private ILocalDetails local = new LocalDetails();  // or something ...
    ...
    public String getServerName() {
        try {
            InetAddress addr = local.getLocalHost();
            return addr.getHostName();
        }
        catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
}

Теперь, чтобы протестировать это, вы создаете «фиктивную» реализацию интерфейса ILocalDetails , чей метод getLocalHost () вызывает исключение. вы хотите при соответствующих условиях. Затем вы создаете единичный текст для SomeClass.getServerName () , устанавливая, что экземпляр SomeClass использует экземпляр вашего «фиктивного» класса вместо обычного. (Последний бит можно сделать с помощью фреймворка mocking, выставив сеттер для local атрибута или с помощью API отражения.)

Очевидно, вам нужно будет изменить ваш код, чтобы сделать его таким тестируемым. И есть ограничения на то, что вы можете делать ... например, теперь вы не можете создать модульный тест, чтобы настоящий метод LocalDetails.getLocalHost () генерировал исключение. Вам необходимо в каждом конкретном случае выносить суждение о том, стоит ли прилагать к этому усилия; то есть перевешивает ли преимущество модульного теста работу (и дополнительную сложность кода) по созданию тестируемого класса таким образом. (Тот факт, что в основе лежит статический метод, является большой частью проблемы.)


** Вот гипотетический момент для такого рода тестирования. В вашем примере тот факт, что исходный код перехватывает исключение и возвращает пустую строку , может быть ошибкой ... в зависимости от того, как указан API метода ... и гипотетический модульный тест выберет это вверх. Однако в этом случае ошибка настолько очевидна, что вы заметите ее при написании модульного теста! И если предположить, что вы исправляете ошибки по мере их обнаружения, модульный тест становится несколько излишним. (Вы не ожидаете, что кто-то повторно установит эту конкретную ошибку ...)

34
ответ дан 27 November 2019 в 17:24
поделиться

Вы можете сообщить junit, что правильным поведением является получение исключения.

В JUnit 4 это выглядит примерно так:

@Test(expected = MyExceptionClass.class) 
public void functionUnderTest() {
    …
}
67
ответ дан 27 November 2019 в 17:24
поделиться

Многие механизмы модульного тестирования позволяют вашим тестам ожидать исключений как часть теста. JUnit, например, позволяет это делать.

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
}
6
ответ дан 27 November 2019 в 17:24
поделиться

Хорошо, здесь есть несколько возможных ответов.

Тестировать на наличие исключения просто.

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

@Test
public void TestForException() {
    try {
        doSomething();
        fail();
    } catch (Exception e) {
        assertThat(e.getMessage(), is("Something bad happened"));
    }
}

В качестве альтернативы вы можете использовать аннотацию исключения, чтобы отметить, что вы ожидаете появления исключения.

Теперь, что касается вашего конкретного примера. Тестирование того, что вы создаете внутри своего метода, либо через new, либо статически, как вы это делали, когда у вас нет возможности взаимодействовать с объектом, сложно. Обычно вам нужно инкапсулировать этот конкретный генератор, а затем использовать некоторую имитацию, чтобы иметь возможность переопределить поведение для генерации ожидаемого исключения.

14
ответ дан 27 November 2019 в 17:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: