Эффективные юнит тесты. Часть I
Aug 6, 2006 · CommentsПрограммированиеТестированиеЮнит тесты
Простой способ сократить число юнит тестов и сохранить 100% покрытие кода - разбить тесты на элементарные случаи и перебрать все комбинации. Этот способ хорошо подходит для тестирования отдельных функций и методов.
Возьмем для примера функцию OpenHandle()
:
HANDLE
OpenThread(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwThreadId);
Результат этой функции зависит как от значения каждого из переданных параметров, так и от внешних условий, например от контекста безопасности (security context) процесса и DACL открываемого потока.
Можно выделить следующие элементарные тесты:
-
Запрашиваемая маска доступа (
dwDesiredAccess
):-
Корректная комбинация флагов;
-
Запрещенная комбинация флагов. Например, один из резервных битов установлен в единицу;
-
-
Флаг, разрешающий наследование описателя порожденным процессом (bInheritHandle):
-
TRUE;
-
FALSE;
-
-
Идентификатор потока (
dwThreadId
):-
Собственного потока;
-
Существующего потока;
-
Несуществующего потока;
-
Потока, который уже завершился;
-
Потока, принадлежащему другому процессу;
-
-
Контекст безопасности потока, в котором вызывается
OpenHandle()
:-
Имеющий полный доступ к открываемому потоку;
-
Имеющий частичный доступ к открываемому потоку;
-
Не имеющий доступ к открываемому потоку;
-
-
И так далее:
Теперь осталось лишь реализовать алгоритм перебора тестов таким образом, чтобы каждая комбинация тестов включала только один тест из каждой категории. Каждой полученной комбинации тестов будет соответствовать один вызов OpenHandle с уникальным набором параметров, включая неявные параметры, такие как состояние внешних по отношению к функции объектов.
Преимущества такого подхода очевидны:
-
Перебор всех вариантов дает хорошее покрытие, приближающееся к 100%;
-
Каждый из элементарных тестов очень прост в реализации;
-
Набор тестов легко расширяется;
-
Легко задать ожидаемый результат каждой комбинации тестов: успешно/неуспешно. Достаточно лишь задать ожидаемый результат для каждого элементарного теста и сложить их используя <Логическое И>.
Не обошлось и без недостатков, главный из которых, - в большинстве случаев не нужно перебирать все разрешенные комбинации параметров. Типичная функция имеет меньше ветвлений, чем количество всех комбинаций. Тем не менее, при таком подходе изменения в тестируемом коде не потребуют новых тестов при условии, что количество параметров не изменилось.
На этом пока все. Пример реализации такого алгоритма будет в следующей части.