Каков наилучший способ тестирования функции, которая вызывает сбой? Или тестирование функции, которая довольно невосприимчива к сбоям?
Например; У меня есть I/O Completion Port
класс, который добавляет конструктор, если он не может правильно инициализировать порт. Это использует Win32
функцию из CreateIoCompletionPort
списка инициализатора. Если дескриптор установлен неправильно - ненулевое значение, - конструктор выдаст исключение. Я никогда не видел, чтобы эта функция не работала.
Я вполне уверен, что эта (и другие функции, подобные ей в моем коде), если они не будут работать, будет работать правильно, код длиной в 50 строк, включая пробелы, поэтому мои вопросы
а) стоит ли проверять, что он скинет
б) и если стоит, то как?
в) должны ли простые классы-обёртки как они быть проверены юнитами?
Для б) я думал о переопределении CreateIoCompletionPort
и передаче значений через. В модульном тесте переопределите его и заставьте возвращать 0 при передаче определенного значения. Однако, поскольку это используется в конструкторе, оно должно быть статическим. Кажется ли это действительным или нет?
Определенно стоит протестировать условия сбоя, поскольку ваш класс правильно генерирует исключение, когда вы этого хотите, и что исключения обрабатываются в классе должным образом.
Это легко сделать, если вы действуете на объекте, переданном конструктору ... просто передайте имитацию. Если нет, я предпочитаю перенести функциональность в защищенный метод и переопределить защищенный метод, чтобы вызвать случай сбоя. Я буду использовать 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, которые вы раскрываете, обычно можно протестировать, и если вы хотите ЗНАТЬ, что исключительные случаи работают так, как должны, вы, вероятно, захотите протестировать их.
Вы должны рассмотреть возможность написания своего кода таким образом, чтобы вы могли смоделировать порт завершения ввода-вывода. Создайте интерфейсный / абстрактный класс, который предоставляет необходимые методы для объекта ввода / вывода, и напишите и протестируйте реализацию, которая делает вещи, как положено (и, возможно, возможность имитировать сбой).
AFAIK - это обычная практика, когда при юнит-тестировании копируют внешние ресурсы, чтобы минимизировать зависимости.
Если вы делаете это в .NET, есть атрибут ExpectedException, который вы можете добавить в свой тест:
[Test, ExpectedException(typeof(SpecificException), "Exception's specific message")]
public void TestWhichHasException()
{
CallMethodThatThrowsSpecificException();
}
Тест пройдет, если будет сгенерировано исключение этого типа и с указанным сообщением. Атрибут имеет другие перегрузки, в том числе наличие InnerExceptions и т. Д.
Звучит как C ++ для меня. Вам нужен шов для макетирования функций Win32. Например, в вашем классе вы должны создать защищенный метод, CreateIoCompletionPort()
который вызывает, ::CreateIoCompletionPort()
и для вашего теста вы создаете класс, который наследуется от вашего класса порта завершения ввода / вывода и переопределяет CreateIoCompletionPort()
ничего, кроме возврата NULL
. Ваш производственный класс по-прежнему ведет себя так, как он был спроектирован, но теперь вы можете смоделировать сбой в CreateIoCompletionPort()
функции.
Эта техника взята из книги Майкла Фезерса «Эффективная работа с устаревшим кодом».