Вы находитесь на странице: 1из 68

Проблема тестовых входных данных

Software Testing 101

Марат Ахин

Санкт-Петербургский политехнический университет

2018

Марат Ахин (СПбПУ) Input 2018 48 / 350


Quiz

Марат Ахин (СПбПУ) Input 2018 49 / 350


Recap

Марат Ахин (СПбПУ) Input 2018 50 / 350


Проблемы тестирования

What’s up, Doc? (с)


Проблема тестовых входных данных
Проблема наблюдаемости
Проблема «останова»
Проблема тестового оракула

Марат Ахин (СПбПУ) Input 2018 51 / 350


Содержание

1 Проблема тестовых входных данных


Обеспечение достижимости
Входные данные
Тестовые данные
Классы эквивалентности

2 Проблема неявных входных данных

3 Разработка через тестирование

4 Интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 52 / 350


Обеспечение достижимости

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

Какой способ можно использовать для обеспечения достижимости


(reachability)?

Марат Ахин (СПбПУ) Input 2018 53 / 350


Обеспечение достижимости

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

Какой способ можно использовать для обеспечения достижимости


(reachability)?

Марат Ахин (СПбПУ) Input 2018 53 / 350


Обеспечение достижимости

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

Какой способ можно использовать для обеспечения достижимости


(reachability)?

Марат Ахин (СПбПУ) Input 2018 53 / 350


Обеспечение достижимости

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

Какой способ можно использовать для обеспечения достижимости


(reachability)?

Марат Ахин (СПбПУ) Input 2018 53 / 350


Входные данные
Что такое входные данные?

Марат Ахин (СПбПУ) Input 2018 54 / 350


Входные данные

Файлы
Фактические аргументы функций
Сетевые пакеты
Результаты запроса к БД
Последовательность вызовов функций
Конфигурация ПО

Марат Ахин (СПбПУ) Input 2018 55 / 350


Тестовые данные

Почему бы просто не перебрать все возможные варианты?

1 int add ( int a , int b ) { ... }

18,446,744,073,709,551,616 вариантов

А если у тестируемого модуля есть внутреннее состояние?

Марат Ахин (СПбПУ) Input 2018 56 / 350


Тестовые данные

Марат Ахин (СПбПУ) Input 2018 57 / 350


Классы эквивалентности

Все пространство входных состояний можно разбить на


множество классов эквивалентности
Каждый класс эквивалентности обрабатывается тестируемым
модулем одинаково с точки зрения спецификации
Тестирование всех классов эквивалентности позволяет найти
ошибки прямого нарушения спецификации

Как найти классы эквивалентности?

Марат Ахин (СПбПУ) Input 2018 58 / 350


Ad hoc testing

Тестирование методом «научного тыка»

Запускаем ПО
Смотрим на результаты работы
...
PROFIT!

Марат Ахин (СПбПУ) Input 2018 59 / 350


Ad hoc testing

А что будет, если я нажму на эту


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

В чем проблемы при таком подходе к тестированию?

Марат Ахин (СПбПУ) Input 2018 60 / 350


Метод свободного поиска

Ad hoc testing + планирование

Описываем тест-план
Проверяем ПО на соответствие тест-плану
...
PROFIT!

Марат Ахин (СПбПУ) Input 2018 61 / 350


Метод свободного поиска

Если ввести число «42», потом


нажать «+», потом ввести «1» и
нажать «=», в результате должно
получиться «43»
Многократные нажатия на «C» не
должны приводить к видимым
изменениям в интерфейсе

В чем проблемы при таком подходе к тестированию?

Марат Ахин (СПбПУ) Input 2018 62 / 350


Анализ граничных значений

Баю баюшки баю, не ложися на краю...

Находим пограничные значения входных данных


Проверяем ПО вокруг выбранных пограничных значений
...
PROFIT!

Марат Ахин (СПбПУ) Input 2018 63 / 350


Анализ граничных значений

Если умножить любое число на «0»,


должен получиться «0»
Деление любого числа на «0»
должно приводить к выводу
соответствующей ошибки
Если изменить знак наибольшего
представимого положительного
числа, должно получиться
соответствующее ему отрицательное
число

В чем проблемы при таком подходе к тестированию?

Марат Ахин (СПбПУ) Input 2018 64 / 350


Примеры

kd-tree
stoi
md5sum
PDF reader

Марат Ахин (СПбПУ) Input 2018 65 / 350


Содержание

1 Проблема тестовых входных данных

2 Проблема неявных входных данных


Неявные входные данные
Использование заглушек

3 Разработка через тестирование

4 Интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 66 / 350


Неявные входные данные

Покрывает ли спецификация все множество входных данных?


В некоторых случаях – да
В большинстве случаев – нет

Почему?
Проблема заключается в том, что на тестовый модуль, кроме
явных, влияет множество неявных входных данных
Неявные входные данные часто не находят отражения в
спецификации

Марат Ахин (СПбПУ) Input 2018 67 / 350


Неявные входные данные

Текущая дата/время
IP/MAC адрес
Локаль пользователя
Идентификаторы устройств
Контекстные переключения/планирование нитей
Скорость поступления IP пакетов
Ритм нажатия клавиш на клавиатуре

Все это – примеры неявных входных данных

Марат Ахин (СПбПУ) Input 2018 68 / 350


Therac-25

Марат Ахин (СПбПУ) Input 2018 69 / 350


Robot pharmacist

Марат Ахин (СПбПУ) Input 2018 70 / 350


Как учесть неявные входные данные?

Какими способами можно управлять выполнением кода?


Изменением входных данных
Изменением самого исходного кода

Simulate and Stub


Заменяем части модуля управляемыми заглушками
Это позволяет сделать неявные входные данные явными
Это также делает управление явными входными данными проще

Марат Ахин (СПбПУ) Input 2018 71 / 350


Использование заглушек

Данный подход позволяет:


имитировать возникновение редких ситуаций
внести детерминизм там, где его нет

Для того, чтобы можно было использовать S&S, тестируемый


модуль должен разрабатываться соответствующим образом

Марат Ахин (СПбПУ) Input 2018 72 / 350


Mock-объекты

Пример S&S – mock-объекты

Повторяют внешний интерфейс тестируемого объекта


Могут демонстрировать любое требуемое поведение

Марат Ахин (СПбПУ) Input 2018 73 / 350


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 2018 74 / 350


Mockito

Сервис аутентификации и авторизации


Вход пользователя с корректными логином/паролем должен
приводить к переходу его учетной записи в активное состояние

Марат Ахин (СПбПУ) Input 2018 75 / 350


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 2018 76 / 350


Mockito

Сервис аутентификации и авторизации


Три последовательные попытки входа пользователя с
некорректным паролем должны приводить к блокированию его
учетной записи

Марат Ахин (СПбПУ) Input 2018 77 / 350


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 2018 78 / 350


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 2018 79 / 350


Mocks 6= Stubs 6= Fakes 6= Dummies

Разве есть разница?

Dummies
Объект-муляж, не обладающий собственным поведением

Fakes
Упрощенная реализация требуемой функциональности

Stubs
Тестовая заглушка, способная отвечать на внешние запросы

Mocks
Тестовая загрушка, способная отвечать на внешние запросы и
проверять их корректность

Марат Ахин (СПбПУ) Input 2018 80 / 350


Содержание

1 Проблема тестовых входных данных

2 Проблема неявных входных данных

3 Разработка через тестирование


Test-driven development
Плюсы TDD
Минусы TDD

4 Интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 81 / 350


Нужны ли заглушки?

А как использовать mock-объекты в процессе разработки?

Для управления неявными входными данными


Для управления явными входными данными

А когда их следует использовать?

Марат Ахин (СПбПУ) Input 2018 82 / 350


Заглушки не нужны?

Никогда!

Зачем тратить время на разработку и использование заглушек?


Можно просто подождать, пока не будут разработаны все
компоненты

Получится проще, дешевле и лучше, чем с заглушками!

Марат Ахин (СПбПУ) Input 2018 83 / 350


Заглушки не нужны?

Все же будет хорошо, да?

Марат Ахин (СПбПУ) Input 2018 84 / 350


Заглушки нужны?

Чем дольше итерация, тем сложнее и дороже исправление ошибок

Чем быстрее будут написаны тесты для разрабатываемого


компонента, тем проще найти ошибки

После разработки компонента пишем для него тест


Отсутствующие части системы заменяем заглушками
...
PROFIT!

Марат Ахин (СПбПУ) Input 2018 85 / 350


Заглушки нужны?

А давайте писать тесты еще быстрее?

Марат Ахин (СПбПУ) Input 2018 86 / 350


Test-driven development

Пишем тест для компонента перед его разработкой


Заменяем сам компонент заглушкой
...
PROFIT???

Разве это будет работать?

Марат Ахин (СПбПУ) Input 2018 87 / 350


Test-driven development

Разве это будет работать?

Марат Ахин (СПбПУ) Input 2018 88 / 350


Плюсы TDD

Разработка ведется небольшими контролируемыми фрагментами

В каждый момент времени разработчик думает об ограниченном


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

Марат Ахин (СПбПУ) Input 2018 89 / 350


Плюсы TDD

Минимальная цена ошибки

В случае возникновения ошибки очень просто вернуть систему в


рабочее состояние
Практически отсутствует необходимость в отладке
Крайне просто использовать метод бисекции в случае, если
ошибка смогла пробраться в релиз

Марат Ахин (СПбПУ) Input 2018 90 / 350


Плюсы TDD

Ошибки обнаруживаются сразу же после их появления

Постоянный запуск тестов гарантирует практически моментальное


обнаружение ошибки
Малый размер тестов позволяет быстро найти причину ошибки

Марат Ахин (СПбПУ) Input 2018 91 / 350


Плюсы TDD

Сильно упрощается рефакторинг кода

Программист уверен в том, что его изменения ничего не ломают


Облегчается раздельное владение кодом

Марат Ахин (СПбПУ) Input 2018 92 / 350


Почему я слышу о TDD впервые в жизни?

Почему TDD не используют везде и всюду?!

Марат Ахин (СПбПУ) Input 2018 93 / 350


Минусы TDD

Синдром «розовых очков»

Большое количество тестов создает иллюзию бесконечной


надежности системы тестирования
При создании теста разработчик может сделать те же допущения,
что и при разработке самого компонента

Марат Ахин (СПбПУ) Input 2018 94 / 350


Минусы TDD

Поддержание тестов в актуальном состоянии

При внесении изменений в интерфейсы компонентов системы


необходимо соответствующим образом изменить и все тесты
Большое количество тестов приводит к значительным затратам
на рефакторинг тестов

Марат Ахин (СПбПУ) Input 2018 95 / 350


Минусы TDD

Невозможность тестирования сложного взаимодействия нескольких


компонентов

Каждый тест проверяет ограниченный фрагмент


функциональности системы
Взаимодействие компонентов затрагивает множество аспектов
системы сразу

Марат Ахин (СПбПУ) Input 2018 96 / 350


Стоит ли использовать TDD?

Марат Ахин (СПбПУ) Input 2018 97 / 350


Содержание

1 Проблема тестовых входных данных

2 Проблема неявных входных данных

3 Разработка через тестирование

4 Интеграционное тестирование
Проблема «Большого Взрыва»
Нисходящее интеграционное тестирование
Восходящее интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 98 / 350


Проблема «Большого Взрыва»

Когда приходит время заменять заглушки на реализацию...

Марат Ахин (СПбПУ) Input 2018 99 / 350


Проблема «Большого Взрыва»

Поведение реализации может (и скорее всего будет) отличаться


от поведения заглушки
Если заглушек было много...

Марат Ахин (СПбПУ) Input 2018 100 / 350


Проблема «Большого Взрыва»

Каскадное распространение сбоев


Сложность локализации ошибок
Большая стоимость исправления ошибок

Что мы можем сделать?

Марат Ахин (СПбПУ) Input 2018 101 / 350


Интеграционное тестирование

Ускорить процесс замены заглушек на реализацию

Выполнять тестирование взаимодействия постоянно, в процессе


разработки
Заменять заглушки на реализацию инкрементально

Инкрементальное интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 102 / 350


Интеграционное тестирование

Марат Ахин (СПбПУ) Input 2018 103 / 350


Нисходящее интеграционное тестирование

Тестирование начинается с верхних уровней системы


Отсутствующие на данный момент модули заменяются
«заглушками»
По мере реализации новых модулей они подключаются к системе
вместо «заглушек»

Марат Ахин (СПбПУ) Input 2018 104 / 350


Нисходящее интеграционное тестирование

Преимущества
Возможность ранней проверки корректности высокоуровневого
поведения
Модули могут добавляться по одному, независимо друг от друга
Не требуется разработка множества драйверов
Можно разрабатывать систему как в глубину, так и в ширину

Марат Ахин (СПбПУ) Input 2018 105 / 350


Нисходящее интеграционное тестирование

Недостатки
Отложенная проверка низкоуровневого поведения
Требуется разработка «заглушек»
Крайне сложно корректно сформулировать требования ко
входам/выходам частичной системы

Марат Ахин (СПбПУ) Input 2018 106 / 350


Восходящее интеграционное тестирование

Тестирование начинается с нижних уровней системы


Отсутствующие на данный момент модули заменяются
драйверами
При реализации всех модулей нижнего уровня драйвер может
быть заменен на соответствующий модуль

Марат Ахин (СПбПУ) Input 2018 107 / 350


Восходящее интеграционное тестирование

Преимущества
Возможность ранней проверки корректности низкоуровневого
поведения
Не требуется написание заглушек
Просто определить требования ко входам/выходам модулей

Марат Ахин (СПбПУ) Input 2018 108 / 350


Восходящее интеграционное тестирование

Недостатки
Отложенная проверка высокоуровневого поведения
Требуется разработка драйверов
При замене драйвера на модуль высокого уровня может
произойти «мини-Большой Взрыв»

Марат Ахин (СПбПУ) Input 2018 109 / 350


Проблемы тестирования

What’s up, Doc? (с)


Проблема тестовых входных данных
Проблема наблюдаемости
Проблема «останова»
Проблема тестового оракула

Марат Ахин (СПбПУ) Input 2018 110 / 350


Обеспечение порчи внутреннего состояния

Для того, чтобы обеспечить порчу внутреннего состояния


(corruption), необходимо:
обеспечить достижимость
передать такие тестовые входные данные, которые вызывают
нарушение целостности внутреннего состояния

См. проблему тестовых входных данных

Марат Ахин (СПбПУ) Input 2018 111 / 350


W.I.L.T.

Марат Ахин (СПбПУ) Input 2018 112 / 350