Тестирование функции, которая генерирует ошибку

Каков наилучший способ тестирования функции, которая вызывает сбой? Или тестирование функции, которая довольно невосприимчива к сбоям?

Например; У меня есть I/O Completion Portкласс, который добавляет конструктор, если он не может правильно инициализировать порт. Это использует Win32функцию из CreateIoCompletionPortсписка инициализатора. Если дескриптор установлен неправильно - ненулевое значение, - конструктор выдаст исключение. Я никогда не видел, чтобы эта функция не работала.

Я вполне уверен, что эта (и другие функции, подобные ей в моем коде), если они не будут работать, будет работать правильно, код длиной в 50 строк, включая пробелы, поэтому мои вопросы

а) стоит ли проверять, что он скинет
б) и если стоит, то как?
в) должны ли простые классы-обёртки как они быть проверены юнитами?

Для б) я думал о переопределении CreateIoCompletionPortи передаче значений через. В модульном тесте переопределите его и заставьте возвращать 0 при передаче определенного значения. Однако, поскольку это используется в конструкторе, оно должно быть статическим. Кажется ли это действительным или нет?

11.08.2008 08:36:39
4 ОТВЕТА

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

Это легко сделать, если вы действуете на объекте, переданном конструктору ... просто передайте имитацию. Если нет, я предпочитаю перенести функциональность в защищенный метод и переопределить защищенный метод, чтобы вызвать случай сбоя. Я буду использовать Java в качестве примера, но это должно быть достаточно просто перенести идеи на C #:

public class MyClass {
    public MyClass() throws MyClassException {
        // Whatever, including a call to invokeCreateIoCompletionPort
    }

    protected int invokeCreateIoCompletionPort(String str, int i) {
        return StaticClass.createIoCompletionPort(str, i);
    }
}

public class MyTest {
    public void myTest() {
        try {
            new MyClass();
            fail("MyClassException was not thrown!");
        } catch (MyClassException e) {
        }
    }

    private static class MyClassWrapper extends MyClass {
        @Override
        protected int invokeCreateIoCompletionPort(String str, int i) {
            throw new ExpectedException();
        }
    }
}

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

По сути, любые детали API, которые вы раскрываете, обычно можно протестировать, и если вы хотите ЗНАТЬ, что исключительные случаи работают так, как должны, вы, вероятно, захотите протестировать их.

2
11.08.2008 09:01:06

Вы должны рассмотреть возможность написания своего кода таким образом, чтобы вы могли смоделировать порт завершения ввода-вывода. Создайте интерфейсный / абстрактный класс, который предоставляет необходимые методы для объекта ввода / вывода, и напишите и протестируйте реализацию, которая делает вещи, как положено (и, возможно, возможность имитировать сбой).

AFAIK - это обычная практика, когда при юнит-тестировании копируют внешние ресурсы, чтобы минимизировать зависимости.

1
11.08.2008 08:57:54

Если вы делаете это в .NET, есть атрибут ExpectedException, который вы можете добавить в свой тест:

[Test, ExpectedException(typeof(SpecificException), "Exception's specific message")]
public void TestWhichHasException()
{
    CallMethodThatThrowsSpecificException();
}

Тест пройдет, если будет сгенерировано исключение этого типа и с указанным сообщением. Атрибут имеет другие перегрузки, в том числе наличие InnerExceptions и т. Д.

3
11.08.2008 09:30:32

Звучит как C ++ для меня. Вам нужен шов для макетирования функций Win32. Например, в вашем классе вы должны создать защищенный метод, CreateIoCompletionPort()который вызывает, ::CreateIoCompletionPort()и для вашего теста вы создаете класс, который наследуется от вашего класса порта завершения ввода / вывода и переопределяет CreateIoCompletionPort()ничего, кроме возврата NULL. Ваш производственный класс по-прежнему ведет себя так, как он был спроектирован, но теперь вы можете смоделировать сбой в CreateIoCompletionPort()функции.

Эта техника взята из книги Майкла Фезерса «Эффективная работа с устаревшим кодом».

0
18.09.2012 13:00:41