4. Проблемы тестирования
Проблемы тестирования
Проблемы тестирования
What’s up, Doc? (с)
Проблема тестовых входных данных
Проблема наблюдаемости
Проблема «останова»
Проблема тестового оракула
Марат Ахин (СПбГПУ)
Input
2014
51 / 321
5. Проблема тестовых входных данных
Содержание
1
Проблема тестовых входных данных
Обеспечение достижимости
Входные данные
Тестовые данные
Классы эквивалентности
2
Проблема неявных входных данных
3
Разработка через тестирование
4
Интеграционное тестирование
Марат Ахин (СПбГПУ)
Input
2014
52 / 321
6. Проблема тестовых входных данных
Обеспечение достижимости
Обеспечение достижимости
Какими способами можно управлять выполнением кода?
Изменением входных данных
Изменением самого исходного кода
Какой способ можно использовать для обеспечения достижимости
(reachability)?
Марат Ахин (СПбГПУ)
Input
2014
53 / 321
7. Проблема тестовых входных данных
Обеспечение достижимости
Обеспечение достижимости
Какими способами можно управлять выполнением кода?
Изменением входных данных
Изменением самого исходного кода
Какой способ можно использовать для обеспечения достижимости
(reachability)?
Марат Ахин (СПбГПУ)
Input
2014
53 / 321
8. Проблема тестовых входных данных
Обеспечение достижимости
Обеспечение достижимости
Какими способами можно управлять выполнением кода?
Изменением входных данных
Изменением самого исходного кода
Какой способ можно использовать для обеспечения достижимости
(reachability)?
Марат Ахин (СПбГПУ)
Input
2014
53 / 321
9. Проблема тестовых входных данных
Обеспечение достижимости
Обеспечение достижимости
Какими способами можно управлять выполнением кода?
Изменением входных данных
Изменением самого исходного кода
Какой способ можно использовать для обеспечения достижимости
(reachability)?
Марат Ахин (СПбГПУ)
Input
2014
53 / 321
10. Проблема тестовых входных данных
Входные данные
Входные данные
Что такое входные данные?
Марат Ахин (СПбГПУ)
Input
2014
54 / 321
11. Проблема тестовых входных данных
Входные данные
Входные данные
Файлы
Фактические аргументы функций
Сетевые пакеты
Результаты запроса к БД
Последовательность вызовов функций
Конфигурация ПО
Марат Ахин (СПбГПУ)
Input
2014
55 / 321
12. Проблема тестовых входных данных
Тестовые данные
Тестовые данные
Почему бы просто не перебрать все возможные варианты?
1 int add ( int a , int b ) { ... }
18,446,744,073,709,551,616 вариантов
А если у тестируемого модуля есть внутреннее состояние?
Марат Ахин (СПбГПУ)
Input
2014
56 / 321
13. Проблема тестовых входных данных
Тестовые данные
Тестовые данные
Марат Ахин (СПбГПУ)
Input
2014
57 / 321
14. Проблема тестовых входных данных
Классы эквивалентности
Классы эквивалентности
Все пространство входных состояний можно разбить на
множество классов эквивалентности
Каждый класс эквивалентности обрабатывается тестируемым
модулем одинаково с точки зрения спецификации
Тестирование всех классов эквивалентности позволяет найти
ошибки прямого нарушения спецификации
Как найти классы эквивалентности?
Марат Ахин (СПбГПУ)
Input
2014
58 / 321
15. Проблема тестовых входных данных
Классы эквивалентности
Ad hoc testing
Тестирование методом «научного тыка»
Запускаем ПО
Смотрим на результаты работы
...
PROFIT!
Марат Ахин (СПбГПУ)
Input
2014
59 / 321
16. Проблема тестовых входных данных
Классы эквивалентности
Ad hoc testing
А что будет, если я нажму на эту
кнопочку?
Если сложить два положительных
числа, то...
После умножения на ноль в
результате должен получится ноль
Если ввести очень большое число и
удвоить его, то...
В чем проблемы при таком подходе к тестированию?
Марат Ахин (СПбГПУ)
Input
2014
60 / 321
17. Проблема тестовых входных данных
Классы эквивалентности
Метод свободного поиска
Ad hoc testing + планирование
Описываем тест-план
Проверяем ПО на соответствие тест-плану
...
PROFIT!
Марат Ахин (СПбГПУ)
Input
2014
61 / 321
18. Проблема тестовых входных данных
Классы эквивалентности
Метод свободного поиска
Если ввести число «42», потом
нажать «+», потом ввести «1» и
нажать «=», в результате должно
получиться «43»
Многократные нажатия на «C» не
должны приводить к видимым
изменениям в интерфейсе
В чем проблемы при таком подходе к тестированию?
Марат Ахин (СПбГПУ)
Input
2014
62 / 321
19. Проблема тестовых входных данных
Классы эквивалентности
Анализ граничных значений
Баю баюшки баю, не ложися на краю...
Находим пограничные значения входных данных
Проверяем ПО вокруг выбранных пограничных значений
...
PROFIT!
Марат Ахин (СПбГПУ)
Input
2014
63 / 321
20. Проблема тестовых входных данных
Классы эквивалентности
Анализ граничных значений
Если умножить любое число на «0»,
должен получиться «0»
Деление любого числа на «0»
должно приводить к выводу
соответствующей ошибки
Если изменить знак наибольшего
представимого положительного
числа, должно получиться
соответствующее ему отрицательное
число
В чем проблемы при таком подходе к тестированию?
Марат Ахин (СПбГПУ)
Input
2014
64 / 321
21. Проблема тестовых входных данных
Классы эквивалентности
Причинно-следственный анализ
А давайте сделаем все по уму!
Строим граф причинно-следственных связей
Полностью покрываем граф тестами
...
PROFIT!
Марат Ахин (СПбГПУ)
Input
2014
65 / 321
22. Проблема тестовых входных данных
Классы эквивалентности
Причинно-следственный анализ
При последовательном выполнении
прямой и обратной операции должен
получиться исходный результат
Операции должны выполняться в
порядке, соответствующем их
приоритету
Последовательные нажатия «=»
должны приводить к повторному
выполнению последней операции над
предыдущим результатом
В чем проблемы при таком подходе к тестированию?
Марат Ахин (СПбГПУ)
Input
2014
66 / 321
23. Проблема тестовых входных данных
Классы эквивалентности
Примеры
kd-tree
stoi
md5sum
PDF reader
Марат Ахин (СПбГПУ)
Input
2014
67 / 321
24. Проблема неявных входных данных
Содержание
1
Проблема тестовых входных данных
2
Проблема неявных входных данных
Неявные входные данные
Использование заглушек
3
Разработка через тестирование
4
Интеграционное тестирование
Марат Ахин (СПбГПУ)
Input
2014
68 / 321
25. Проблема неявных входных данных
Неявные входные данные
Неявные входные данные
Покрывает ли спецификация все множество входных данных?
В некоторых случаях – да
В большинстве случаев – нет
Почему?
Проблема заключается в том, что на тестовый модуль, кроме
явных, влияет множество неявных входных данных
Неявные входные данные часто не находят отражения в
спецификации
Марат Ахин (СПбГПУ)
Input
2014
69 / 321
26. Проблема неявных входных данных
Неявные входные данные
Неявные входные данные
Текущая дата/время
IP/MAC адрес
Локаль пользователя
Идентификаторы устройств
Контекстные переключения/планирование нитей
Скорость поступления IP пакетов
Ритм нажатия клавиш на клавиатуре
Все это – примеры неявных входных данных
Марат Ахин (СПбГПУ)
Input
2014
70 / 321
27. Проблема неявных входных данных
Неявные входные данные
Therac-25
Марат Ахин (СПбГПУ)
Input
2014
71 / 321
28. Проблема неявных входных данных
Использование заглушек
Как учесть неявные входные данные?
Какими способами можно управлять выполнением кода?
Изменением входных данных
Изменением самого исходного кода
Simulate and Stub
Заменяем части модуля управляемыми заглушками
Это позволяет сделать неявные входные данные явными
Это также делает управление явными входными данными проще
Марат Ахин (СПбГПУ)
Input
2014
72 / 321
29. Проблема неявных входных данных
Использование заглушек
Использование заглушек
Данный подход позволяет:
имитировать возникновение редких ситуаций
внести детерминизм там, где его нет
Для того, чтобы можно было использовать S&S, тестируемый
модуль должен разрабатываться соответствующим образом
Марат Ахин (СПбГПУ)
Input
2014
73 / 321
30. Проблема неявных входных данных
Использование заглушек
Mock-объекты
Пример S&S – mock-объекты
Повторяют внешний интерфейс тестируемого объекта
Могут демонстрировать любое требуемое поведение
Марат Ахин (СПбГПУ)
Input
2014
74 / 321
31. Проблема неявных входных данных
Использование заглушек
Mockito
1 @Test
2 void testFoo () {
3
List mockedList = mock ( List . class );
4
5
when ( mockedList . get (0)). thenReturn ( " first " );
6
when ( mockedList . get (1)). thenReturn ( " second " );
7
8
assertEquals ( " first " , mockedList . get (0));
9
assertEquals ( " second " , mockedList . get (1));
10
assertEquals ( null ,
mockedList . get (42));
11 }
Марат Ахин (СПбГПУ)
Input
2014
75 / 321
32. Проблема неявных входных данных
Использование заглушек
Mockito
Сервис аутентификации и авторизации
Вход пользователя с корректными логином/паролем должен
приводить к переходу его учетной записи в активное состояние
Марат Ахин (СПбГПУ)
Input
2014
76 / 321
33. Проблема неявных входных данных
Использование заглушек
Mockito
1 @Test
2 void t e s t L o g i n F o r V a l i d U s e r () {
3
IPasswordInfo passInfo = mock ( IPasswordInfo . class );
4
when ( passInfo . matches ( anyString ())). thenReturn ( true );
5
6
IAccount account = mock ( IAccount . class );
7
when ( account . ge t Pa ss w or d In fo ()). thenReturn ( passInfo );
8
9
I A c c o u n t R e p o s i t o r y repo = mock ( I A c c o u n t R e p o s i t o r y . class );
10
when ( repo . find ( anyString () , anyString ())). thenReturn ( account );
11
12
AuthService service = new AuthService ( repo );
13
service . login ( " marat " , " password2 " );
14
15
verify ( account ). setLoggedIn ( true );
16 }
Марат Ахин (СПбГПУ)
Input
2014
77 / 321
34. Проблема неявных входных данных
Использование заглушек
Mockito
Сервис аутентификации и авторизации
Три последовательные попытки входа пользователя с
некорректным паролем должны приводить к блокированию его
учетной записи
Марат Ахин (СПбГПУ)
Input
2014
78 / 321
35. Проблема неявных входных данных
Использование заглушек
Mockito
1 @Test
2 void t e s t B l o c k O n I n c o r r e c t L o g i n () {
3
IPasswordInfo passInfo = mock ( IPasswordInfo . class );
4
when ( passInfo . matches ( anyString ())). thenReturn ( false );
5
6
IAccount account = mock ( IAccount . class );
7
when ( account . ge t Pa ss w or d In fo ()). thenReturn ( passInfo );
8
9
I A c c o u n t R e p o s i t o r y repo = mock ( I A c c o u n t R e p o s i t o r y . class );
10
when ( repo . find ( anyString () , anyString ())). thenReturn ( account );
11
12
AuthService service = new AuthService ( repo );
13
service . login ( " bob " , " 111 " );
14
service . login ( " bob " , " 123 " );
15
service . login ( " bob " , " 321 " );
16
17
verify ( account ). setBlocked ( true );
18 }
Марат Ахин (СПбГПУ)
Input
2014
79 / 321
36. Проблема неявных входных данных
Использование заглушек
Mockito
1 @Test
2 void t e s t B l o c k O n I n c o r r e c t L o g i n P e r A c c o u n t () {
3
IPasswordInfo aliceInfo = g e t M o c k e d P a s s w o r d I n f o ( false );
4
IPasswordInfo bobInfo = g e t M o c k e d P a s s w o r d I n f o ( false );
5
6
IAccount aliceAcc = g e t M o c k e d A c c o u n t ( aliceInfo );
7
IAccount bobAcc = g e t M o c k e d A c c o u n t ( bobInfo );
8
9
I A c c o u n t R e p o s i t o r y repo = mock ( I A c c o u n t R e p o s i t o r y . class );
10
when ( repo . find ( " alice " , anyString ())). thenReturn ( aliceAcc );
11
when ( repo . find ( " bob " , anyString ())). thenReturn ( bobAcc );
12
13
AuthService service = new AuthService ( repo );
14
service . login ( " bob " ,
" 111 " );
15
service . login ( " alice " , " 111 " );
16
service . login ( " bob " ,
" 123 " );
17
service . login ( " alice " , " 123 " );
18
service . login ( " bob " ,
" 321 " );
19
20
verify ( aliceAcc , never ()). setBlocked ( true );
21
verify ( bobAcc , times (1)). setBlocked ( true );
22 }
Марат Ахин (СПбГПУ)
Input
2014
80 / 321
37. Проблема неявных входных данных
Использование заглушек
Mocks = Stubs = Fakes = Dummies
Разве есть разница?
Dummies
Объект-муляж, не обладающий собственным поведением
Fakes
Упрощенная реализация требуемой функциональности
Stubs
Тестовая заглушка, способная отвечать на внешние запросы
Mocks
Тестовая загрушка, способная отвечать на внешние запросы и
проверять их корректность
Марат Ахин (СПбГПУ)
Input
2014
81 / 321
38. Разработка через тестирование
Содержание
1
Проблема тестовых входных данных
2
Проблема неявных входных данных
3
Разработка через тестирование
Test-driven development
Плюсы TDD
Минусы TDD
4
Интеграционное тестирование
Марат Ахин (СПбГПУ)
Input
2014
82 / 321
39. Разработка через тестирование
Test-driven development
Нужны ли заглушки?
А как использовать mock-объекты в процессе разработки?
Для управления неявными входными данными
Для управления явными входными данными
А когда их следует использовать?
Марат Ахин (СПбГПУ)
Input
2014
83 / 321
40. Разработка через тестирование
Test-driven development
Заглушки не нужны?
Никогда!
Зачем тратить время на разработку и использование заглушек?
Можно просто подождать, пока не будут разработаны все
компоненты
Получится проще, дешевле и лучше, чем с заглушками!
Марат Ахин (СПбГПУ)
Input
2014
84 / 321
42. Разработка через тестирование
Test-driven development
Заглушки нужны?
Чем дольше итерация, тем сложнее и дороже исправление ошибок
Чем быстрее будут написаны тесты для разрабатываемого
компонента, тем проще найти ошибки
После разработки компонента пишем для него тест
Отсутствующие части системы заменяем заглушками
...
PROFIT!
Марат Ахин (СПбГПУ)
Input
2014
86 / 321
44. Разработка через тестирование
Test-driven development
Test-driven development
Пишем тест для компонента перед его разработкой
Заменяем сам компонент заглушкой
...
PROFIT???
Разве это будет работать?
Марат Ахин (СПбГПУ)
Input
2014
88 / 321
46. Разработка через тестирование
Плюсы TDD
Плюсы TDD
Разработка ведется небольшими контролируемыми фрагментами
В каждый момент времени разработчик думает об ограниченном
фрагменте спецификации
Это упрощает анализ возможного пространства входных данных
Кроме того, код получается более модульным и расширяемым
Марат Ахин (СПбГПУ)
Input
2014
90 / 321
47. Разработка через тестирование
Плюсы TDD
Плюсы TDD
Минимальная цена ошибки
В случае возникновения ошибки очень просто вернуть систему в
рабочее состояние
Практически отсутствует необходимость в отладке
Крайне просто использовать метод бисекции в случае, если
ошибка смогла пробраться в релиз
Марат Ахин (СПбГПУ)
Input
2014
91 / 321
48. Разработка через тестирование
Плюсы TDD
Плюсы TDD
Ошибки обнаруживаются сразу же после их появления
Постоянный запуск тестов гарантирует практически моментальное
обнаружение ошибки
Малый размер тестов позволяет быстро найти причину ошибки
Марат Ахин (СПбГПУ)
Input
2014
92 / 321
49. Разработка через тестирование
Плюсы TDD
Плюсы TDD
Сильно упрощается рефакторинг кода
Программист уверен в том, что его изменения ничего не ломают
Облегчается раздельное владение кодом
Марат Ахин (СПбГПУ)
Input
2014
93 / 321
50. Разработка через тестирование
Плюсы TDD
Почему я слышу о TDD впервые в жизни?
Почему TDD не используют везде и всюду?!
Марат Ахин (СПбГПУ)
Input
2014
94 / 321
51. Разработка через тестирование
Минусы TDD
Минусы TDD
Синдром «розовых очков»
Большое количество тестов создает иллюзию бесконечной
надежности системы тестирования
При создании теста разработчик может сделать те же допущения,
что и при разработке самого компонента
Марат Ахин (СПбГПУ)
Input
2014
95 / 321
52. Разработка через тестирование
Минусы TDD
Минусы TDD
Поддержание тестов в актуальном состоянии
При внесении изменений в интерфейсы компонентов системы
необходимо соответствующим образом изменить и все тесты
Большое количество тестов приводит к значительным затратам
на рефакторинг тестов
Марат Ахин (СПбГПУ)
Input
2014
96 / 321
53. Разработка через тестирование
Минусы TDD
Минусы TDD
Невозможность тестирования сложного взаимодействия нескольких
компонентов
Каждый тест проверяет ограниченный фрагмент
функциональности системы
Взаимодействие компонентов затрагивает множество аспектов
системы сразу
Марат Ахин (СПбГПУ)
Input
2014
97 / 321
55. Интеграционное тестирование
Содержание
1
Проблема тестовых входных данных
2
Проблема неявных входных данных
3
Разработка через тестирование
4
Интеграционное тестирование
Проблема «Большого Взрыва»
Нисходящее интеграционное тестирование
Восходящее интеграционное тестирование
Марат Ахин (СПбГПУ)
Input
2014
99 / 321
57. Интеграционное тестирование
Проблема «Большого Взрыва»
Проблема «Большого Взрыва»
Поведение реализации может (и скорее всего будет) отличаться
от поведения заглушки
Если заглушек было много...
Марат Ахин (СПбГПУ)
Input
2014
101 / 321
58. Интеграционное тестирование
Проблема «Большого Взрыва»
Проблема «Большого Взрыва»
Каскадное распространение сбоев
Сложность локализации ошибок
Большая стоимость исправления ошибок
Что мы можем сделать?
Марат Ахин (СПбГПУ)
Input
2014
102 / 321
59. Интеграционное тестирование
Проблема «Большого Взрыва»
Интеграционное тестирование
Ускорить процесс замены заглушек на реализацию
Выполнять тестирование взаимодействия постоянно, в процессе
разработки
Заменять заглушки на реализацию инкрементально
Инкрементальное интеграционное тестирование
Марат Ахин (СПбГПУ)
Input
2014
103 / 321
61. Интеграционное тестирование
Нисходящее интеграционное тестирование
Нисходящее интеграционное тестирование
Тестирование начинается с верхних уровней системы
Отсутствующие на данный момент модули заменяются
«заглушками»
По мере реализации новых модулей они подключаются к системе
вместо «заглушек»
Марат Ахин (СПбГПУ)
Input
2014
105 / 321
62. Интеграционное тестирование
Нисходящее интеграционное тестирование
Нисходящее интеграционное тестирование
Преимущества
Возможность ранней проверки корректности высокоуровневого
поведения
Модули могут добавляться по одному, независимо друг от друга
Не требуется разработка множества драйверов
Можно разрабатывать систему как в глубину, так и в ширину
Марат Ахин (СПбГПУ)
Input
2014
106 / 321
63. Интеграционное тестирование
Нисходящее интеграционное тестирование
Нисходящее интеграционное тестирование
Недостатки
Отложенная проверка низкоуровневого поведения
Требуется разработка «заглушек»
Крайне сложно корректно сформулировать требования ко
входам/выходам частичной системы
Марат Ахин (СПбГПУ)
Input
2014
107 / 321
64. Интеграционное тестирование
Восходящее интеграционное тестирование
Восходящее интеграционное тестирование
Тестирование начинается с нижних уровней системы
Отсутствующие на данный момент модули заменяются
драйверами
При реализации всех модулей нижнего уровня драйвер может
быть заменен на соответствующий модуль
Марат Ахин (СПбГПУ)
Input
2014
108 / 321
65. Интеграционное тестирование
Восходящее интеграционное тестирование
Восходящее интеграционное тестирование
Преимущества
Возможность ранней проверки корректности низкоуровневого
поведения
Не требуется написание заглушек
Просто определить требования ко входам/выходам модулей
Марат Ахин (СПбГПУ)
Input
2014
109 / 321
66. Интеграционное тестирование
Восходящее интеграционное тестирование
Восходящее интеграционное тестирование
Недостатки
Отложенная проверка высокоуровневого поведения
Требуется разработка драйверов
При замене драйвера на модуль высокого уровня может
произойти «мини-Большой Взрыв»
Марат Ахин (СПбГПУ)
Input
2014
110 / 321
67. Проблемы тестирования
Проблемы тестирования
Проблемы тестирования
What’s up, Doc? (с)
Проблема тестовых входных данных
Проблема наблюдаемости
Проблема «останова»
Проблема тестового оракула
Марат Ахин (СПбГПУ)
Input
2014
111 / 321
68. Проблемы тестирования
Проблемы тестирования
Обеспечение порчи внутреннего состояния
Для того, чтобы обеспечить порчу внутреннего состояния
(corruption), необходимо:
обеспечить достижимость
передать такие тестовые входные данные, которые вызывают
нарушение целостности внутреннего состояния
См. проблему тестовых входных данных
Марат Ахин (СПбГПУ)
Input
2014
112 / 321