3. Зачем тестирование нужно?
1
Полностью протестировать любую программу.
2
Тестирование позволит убедиться, что программа
работает правильно.
3
Тестировщик должен гарантировать правильность
выполнения программы.
6. Стандартные возражения
Почему программисты вообще делают ошибки? Пусть они их
не делают.
Сначала все напишем по-быстрому, а потом поправим все
ошибки.
Сколько ошибок мы нашли? N – это много или мало?
Почему наши пользователи находят ошибки, если мы
потратили на тестирование столько времени?!
Мы нашли N ошибок – мы можем остановить тестирование?
А как будет себя вести наше приложение в эксплуатации?
Заблуждения – это реакция на неудовлетворительные
ответы на данные вопросы.
7. Полностью протестировать любую
программу невозможно
• Проверить реакцию программы на каждую комбинацию
входных данных
– Есть корректные и некорректные входные данные
• Проверить каждую возможную последовательность
выполнения команд программы
– Мейерс, 1979
Написал программу из 100 строк, имеющую 108
последовательностей выполнения
8. Тестирование позволит убедиться,
что программа работает правильно
• Правильность программы нельзя доказать логически
– Можно проверить только соответствие спецификации
9. Тестировщик должен гарантировать
правильность выполнения
программы
• 40-80% времени тратится на исправление ошибок
• 1,5 ошибки на один оператор программы
• Тестировщик тоже человек!
12. Что такое качество?
• Качество ≠ отсутствие ошибок
В самом качественном автомобиле 2010 года выявляется в среднем 59
дефектов на 100 автомобилей в первые 90 дней.
• Удовлетворенность заказчика?
Пациент, которому действия доктора причиняют боль.
• Соответствие ожиданиям: делает то, что должен, не
делает того, что не должен
13. Определение качества
ISO 9126
Качество ПО – совокупная характеристика ПО с учетом
следующих составляющих:
–
–
–
–
–
–
Надежность
Сопровождаемость
Практичность
Эффективность
Мобильность
Функциональность
14. Определение качества
Надежность – набор атрибутов, относящихся к способности
ПО сохранять свой уровень качества функционирования в
установленных условиях за определенный период
времени
• Уровень завершенности
(отсутствие ошибок)
• Устойчивость к дефектам
• Восстанавливаемость
• Доступность
• Готовность
15. Определение качества
Мобильность — набор атрибутов, относящихся к
способности ПО быть перенесенным из одного окружения
в другое
• Адаптируемость
• Простота установки
• Сосуществование
• Замещаемость
16. Определение качества
Сопровождаемость - набор атрибутов, относящихся к
объему работ, требуемых для проведения конкретных
изменений (модификаций)
• Удобство анализа
• Изменяемость
• Стабильность
• Тестируемость
17. Определение качества
Практичность (применимость) — набор атрибутов,
относящихся к объему работ, требуемых для исполнения
и индивидуальной оценки такого исполнения
определенным или предполагаемым кругом
пользователей
• Понятность
• Простота использования
• Изучаемость
• Привлекательность
18. Определение качества
Эффективность — набор атрибутов, относящихся к
соотношению между уровнем качества
функционирования ПО и объемом используемых ресурсов
при установленных условиях
• Временная эффективность
• Используемость ресурсов
19. Определение качества
Функциональность — набор атрибутов характеризующий,
соответствие функциональных возможностей ПО набору
требуемой пользователем функциональности
• Пригодность к применению
• Корректность
• Способность к взаимодействию
• Защищенность
20. Как получается Качество ПО
• Качество ПО зависит только от качества его процесса
разработки
• Качество процесса разработки зависит только от культуры
разработки самой проектной команды
21. Определение тестирования
Тестирование ПО - процесс исследования программного
обеспечения (ПО) с целью получения информации о
качестве продукта.
(Толковый словарь)
Дефект - изъян, порча, повреждение; недостаток,
несовершенство.
29. Соберем идеи
• Тестирование ПО больше, чем просто поиск дефектов!!!
• Тестирование – это часть процесса разработки, которое
занимает 40-80% всего процесса разработки и стоит 3050% от общей стоимости проекта
• Тестирование не только может выявлять дефекты, но и
предотвращать их.
• Эффективность тестирования сильно влияет на стоимость
проекта и сроки его завершения.
• Отказ от тестирования – это стратегия пассивного
принятия рисков.
• Автоматизация тестирования должна приводить к
снижению расходов на тестирование.
30. Классификация видов
тестирования
По объекту тестирования:
• Функциональное тестирование
• Тестирование производительности
– Нагрузочное тестирование
– Тестирование стабильности
– Стресс-тестирование
•
•
•
•
•
Тестирование удобства использования
Тестирование интерфейса пользователя
Тестирование безопасности
Тестирование локализации
Тестирование совместимости
31. По знанию системы:
• Тестирование белого ящика
– Соответствие кода соглашениям по наименованию и по
кодированию
– Корректная обработка ошибок
– Выделение памяти
– Покрытие кода: все ли операторы были выполнены хотя бы один
раз
– Проверка всех путей управления модуля
• Тестирование черного ящика
• Тестирование серого ящика
По степени автоматизации:
• Ручное тестирование
• Автоматизированное тестирование
• Полуавтоматизированное тестирование
32. По степени автоматизации:
• Ручное тестирование
• Автоматизированное тестирование
• Полуавтоматизированное тестирование
По степени изолированности системы:
• Модульное тестирование
• Интеграционное тестирование
• Системное тестирование
33. По степени позитивности сценариев:
• Позитивное тестирование
• Негативное тестирование
По степени подготовленности к тестированию:
• Тестирование по документации (формальное
тестирование)
• Тестирование как есть (интуитивное тестирование)
34. По времени проведения тестирования:
• Альфа-тестирование
–
–
–
–
Тестирование при приемке
Тестирование новой функциональности
Регрессионное тестирование
Тестирование при сдаче
• Бета-тестирование
• Приемочное тестирование
35. Логика Хоара
• Контрактная модель программирования
• Используется для доказательства частичной и полной
корректности компьютерных программ
• Тройка Хоара {pred} statement {post}, где pred, post –
утверждения (assertions), pred – предусловие, post постусловие, statement – оператор языка программирования
Пример:
{x+1 == 43} y=x+1; {y == 43 ^ x == 42}
• Логика Хоара определяет аксиомы и правила вывода для
императивного языка программирования
(http://ru.wikipedia.org/wiki/Логика_Хоара)
36. Правила Логики Хоара
Аксиома пустого оператора
{P} skip {P}
Аксиома присваивания
{P[E/x]} x := E {P}
Правило композиции
{P} S {Q}, {Q} T {R} ╞ {P} S;T {R}
Правило условного оператора
{B ^ P} S {Q}, {B’ ^P} T {Q} ╞ {P} if B then S else T endif {Q}
Правило вывода
P1 → P, {P} S {Q}, Q → Q1 ╞ {P1} S {Q1}
Правило оператора цикла
{P ^ B} S {P} ╞ {P} while B do S done {B’ ^ P}
37. От формальной системы к
автоматическому тестированию
• Все последовательности операторов протестировать
невозможно
• Часто тестирование – это многократно повторяющиеся
рутинные действия
• Человек преимущественно необходим только на этапе
оценки результатов, выданных ПО, на их соответствие
требованиям заказчика.
• Часто проверку результатов на соответствие требованиям
заказчика также можно автоматизировать.
39. Модульное (юнит)
тестирование
• С помощью правила композиции и/или правила вывода
объединяем несколько операторов в один общий блок.
Проверка предусловия и постусловия для блока
операторов равносильна проверке пост- и предусловий
каждого из операторов в этом блоке.
Пример:
{x + 1 ==43 } y = x + 1; {y == 43}
{y == 43} z = y; {z == 43}
По правилу композиции
{x + 1 == 43} y = x + 1; z = y; {z == 43}
40. • Какие границы блоков удобнее выбирать для целей
тестирования?
– Методы (см. Контрактная модель программирования)
{pred} void f(object arg1)… {post}
– Классы (как контейнер методов)
• Часто при разработке кода делаются неявные
предположения о состоянии программы (Asserts)
Исследования: На каждые 5-6 строк делается как минимум одно
неявное предположение о состоянии программы
Вывод: Рекомендация по рефакторингу – методы должны
быть короткими (5-7 строк)
41. Следствия
• Пишутся на том же языке, что и тестируемая процедура или класс
• Юнит-тесты должны быть простыми!!!
Плюсы
• Стимулируют рефакторинг
• Поощряют написание слабосвязанного кода
• Упрощают регрессионное тестирование
• Юнит-тесты = документация
Ограничения
• Не проверяют взаимодействие компонентов
• Не дают 100% гарантии
• Сложно покрывать тестами уже написанное приложение
42. Интеграционное тестирование с
точки зрения автоматизации
• Есть несколько методов, каждый из которых покрыт
тестами
• Эти методы используются для реализации функции
верхнего уровня
• Покрытие тестами этой функции и есть интеграционное
тестирование
Замечание: Покрытие тестами только
верхнеуровневой функции не
принесет много пользы, т.к. не
проведено модульное тестирование
самой функции, а значит нет
возможности локализовать ошибку
43. Критерий остановки
• На один метод как правило пишется несколько тестов
Когда пора остановиться?
Варианты:
• По времени – очень плохой критерий
• Вычислять коэффициент покрытия кода – не всегда есть
под рукой инструменты для вычисления покрытия кода,
нет 100% гарантии
• Test Driven Development
46. Критерий “хорошего” теста
• Существует обоснованная вероятность выявления тестом
ошибки
• Набор тестов не должен быть избыточным
• Тест должен быть наилучшим в своей категории
• Не должен быть слишком простым и слишком сложным
50. Классы эквивалентности
Если при выполнении двух тестов ожидается один и тот же
результат, то они считаются эквивалентными.
Группа тестов представляет собой класс эквивалентности,
если:
51. Классы эквивалентности
• Все тесты предназначены для выявления одной и той же
ошибки
• Если один из тестов выявит ошибку, то остальные скорее
всего тоже это сделают
• Если один из тестов не выявит ошибки, то остальные
скорее всего тоже этого не сделают
52. Классы экивалентности
• Тесты включают значения одних и тех же входных данных
• Для их проведения выполняются одни и те же операции
• В результате тестов формируются значения одних и тех же
выходных данных
• Либо не один из тестов не вызывает обработку ошибок в
программе, либо всеми тестами вызывается одна и та же
обработка ошибок
53. Пример классов
эквивалентности
Решение квадратного уравнения в вещественных числах
Result SquareRoot(double a, double b, double c);
• Уравнение, имеющее решение в виде двух корней
кратности 1 – (1, -1, -2)
• Уравнение, имеющее решение в виде одного корня
кратности 2 – (1, 2, 1)
• Уравнение, не имеющее решений – (1, 0, 1)
• Коэффициент a = 0
56. • Диапазоны числовых значений
I. Для диапазона значений есть три недопустимых класса
экивалентности:
– Числа меньше диапазона
– Числа больше диапазона
– Нечисловые данные
Замечание: Иногда некоторые из этих классов могут отсутствовать,
например, нет ограничения сверху. Лучше явно убедиться, что
этого ограничения нет, взяв очень большое число.
II. Поддиапазоны, например, 1-5, 9, 10, 12, 14, 16 – этажность зданий
57. • Фиксированные перечни значений
– Все значения данного перечня
– Любое значение, не входящее в перечень
Например, улицы г. Омска
Идеи для поиска классов эквивалентности
– Аббревиатуры
– Сокращения
– Ошибки при написании
– Старые названия
58. • Списки меню, выбора
Любой элемент меню может представлять отдельный класс
эквивалентности
• Переменные, значения которых должны быть равными
Значения, которые являются приемлемыми, но не принимаются в
данном месте программы, образуют отдельные эквивалентности
Пример: дата 13-07-2011, 07-13-2011, 13.07.2011, 13/07/2011 числа с
плавающей точкой 12,43 12.43
59. • Значения, зависящие от времени
Пример: запрос серверу, когда
– Ничего не обрабатывается
– Идет обработка другого запроса
– Сразу после завершения обработки другого запроса
• Группы переменных, результат которых ограничивается
определенным набором или диапазоном значений
Пример: квадратное уравнение
60. • Действия, на которые программа отвечает
эквивалентными событиями
Пример, обработка ошибок
• Различные варианты окружения
Пример, объем оперативный памяти:
– Проектный
– Недостаточный
– Больше, чем проектный
61. Граничные условия
• Для каждого класса эквивалентности нужно провести
минимальное число тестов
Следовательно: Лучшие тесты – те, которые лежат на границах
классов эквивалентности
Пример, для квадратного уравнения лучше брать не 0, а число, чуть
меньше “эпсилон”
63. Stubs
Зависимость – это объект системы, с котором
взаимодействует тестируемый код и над которым нет
контроля у разработчика тестов.
Примеры:
– Объекты операционной системы
– Объекты, разработка которых еще не была завершена/начата
– Сторонние библиотеки
Заглушка – контролируемая замена зависимости в
разрабатываемой системе
64. Когда применяются
заглушки
• Создать имитацию загрузки или ограниченности ресурсов
• Создать имитацию сбоя в системе
• Выполнить тестирование, когда зависимость еще не
реализована
• Гарантировать отсутствие влияния зависимости на
результаты тестирования
• Отладка тестов для фасада
65. Идеи по реализации
заглушек
• Заглушка имитирует поведение реального объекта –
применяем паттерн Прокси
• Код должен, использующий базовый интерфейс для
Заглушек и реальных объектов, должен удовлетворять
принципу подстановки Лисков
• Тестируемые классы должны либо конфигурироваться
интерфейсами, либо использовать фабрики
66. Mocks
Тестирование основанное на состояниях (традиционный
подход)
Недостатки:
– Требует знания о внутреннем состоянии объекта –возможно
нарушение инкапсуляции
– Нарушает принципы ООП
67. Тестирование основанное на поведении – сосредоточено на
том, какие методы вызываются, в какой
последовательности, какие результаты выдают
Поведенческое тестирование – тестирование того, как
объект взаимодействует с другими объектами, то
есть посылает выходные данные и принимает входные
от других объектов.
68. Mock-объект – прокси-объект, который определяет
прошел ли тестируемый объект тест или нет
Различие между Stub и Mock:
• Stub подменяет реальный объект с целью имитации
реального функционала
• Mock подменяет реальный объект с целью оценки
правильности взаимодействия тестируемого объекта с
подменяемым.
69. Подходы к реализации mock
• Proxy (easymock, jmock, moq, rhino.mocks)
• Подгрузка класса mock объекта вместо реального класса
(jmockit, powermock, DI контейнеры)
70. Библиотека EasyMock
IFoo mock = EasyMock.createMock(IFoo.class);
EasyMock.expect(mock.doSomething(“argument”)).andReturn(t
rue);
EasyMock.replay(mock);
bool result = test.perform();
EasyMock.verify(mock);
75. Библиотека Moq
Методы
1. Вызов метода
var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);
2. Выходные параметры
var outString = "ack";
mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true);
3. Входно-выходные параметры
var instance = new Bar();
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);
77. 7. Возврат разных значений на каждый вызов функции
var mock = new Mock<IFoo>();
var calls = 0;
mock.Setup(foo => foo.GetCountThing())
.Returns(() => calls)
.Callback(() => calls++);
Console.WriteLine(mock.Object.GetCountThing());
78. 8. Возращение значений в зависимости от значений входных
параметров
mock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(true);
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true);
mock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10,
Range.Inclusive))).Returns(true);
mock.Setup(x => x.DoSomething(It.IsRegex("[a-d]+",
RegexOptions.IgnoreCase))).Returns("foo");
82. Проверки
1. Вызов метода
mock.Verify(foo => foo.Execute("ping"));
mock.Verify(foo => foo.Execute("ping"), "When doing operation X, the
service should be pinged always");
2. Количество вызовов метода
mock.Verify(foo => foo.Execute("ping"), Times.Never());
mock.Verify(foo => foo.Execute("ping"), Times.AtLeastOnce());
84. Пример: восстановление
пароля – шаг 1
public void When_user_forgot_password_should_save_user()
{
var stubUserRepository = mockRepository.Create<IUserRepository>();
var stubbedSmsSender = mockRepository.Create<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Setup(x => x.GetUserByName("ayende")).Return(theUser).Verifiable();
var controllerUnderTest = new LoginController(stubUserRepository.Object, stubbedSmsSender.Object);
controllerUnderTest.ForgotMyPassword("ayende");
stubUserRepository.AssertWasCalled( x => x.Save(user));
}
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
users.Save(user);
}
85. Пример: восстановление
пароля – шаг 2
public void When_user_forgot_password_should_reset_password()
{
var stubUserRepository = mockRepository.Create<IUserRepository>();
var stubbedSmsSender = MockRepository.Create<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
stubUserRepository.Setup(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository.Object, stubbedSmsSender.Object);
controllerUnderTest.ForgotMyPassword("ayende");
Assert.AreNotEqual("this is not hashed password",
theUser.HashedPassword);
}
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
}
86. Пример: восстановление
пароля – шаг 3
public void When_user_forgot_password_should_save_user()
{
var mockUserRepository = mockRepository.Create<IUserRepository>();
var stubbedSmsSender = mockRepository.Create<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password"};
mockUserRepository.Setup(x => x.GetUserByName("ayende")).Return(theUser);
mockUserRepository.Setup ( x => x.Save(theUser) ).Verifiable();
var controllerUnderTest = new LoginController(mockUserRepository.Object, stubbedSmsSender.Object);
controllerUnderTest.ForgotMyPassword("ayende");
mockUserRepository.VerifyAll();
}
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
}
87. Пример: восстановление
пароля – шаг 4
public void When_user_forgot_password_should_sms_user()
{
var stubUserRepository = mockRepository.Create<IUserRepository>();
var stubbedSmsSender = mockRepository.Create<ISmsSender>();
var theUser = new User{HashedPassword = "this is not hashed password", Phone = "1234-1234"};
stubUserRepository.Stub(x => x.GetUserByName("ayende")).Return(theUser);
var controllerUnderTest = new LoginController(stubUserRepository.Object, stubbedSmsSender.Object);
controllerUnderTest.ForgotMyPassword("ayende");
stubbedSmsSender.AssertWasCalled( x => x.Send( Arg.Is.Equal("1234-1234"),
Arg.Text.StartsWith("Password was changed to:") ));
}
public void ForgotMyPassword(string username)
{
var user = users.GetUserByName(username);
user.HashedPassword = "new pass";
users.Save(user);
smsSender.Send(user.Phone, "Password was changed to: new pass");
}
88. Пример: заглушка для еще
не написанного объекта
Interface IUserStorage
{
bool Save(User user);
bool Connect(string userName);
}
89. Пример: заглушка для еще
не написанного объекта
class AbstractFactory
{
IUserStorage Create()
{
Mock<IUserStorage> storage = mockRepository.Create<IUserStrorage>();
storage.Setup(s => s.Save(It.IsAny<User>())).Returns(true);
storage.Setup(s => s.Connect(“ayende”)).Returns(true);
storage.Setup(s => s.Connect(It.Not.IsEqual<string>(“ayende”))).Returns(false);
return storage.Object;
}
MockRepository mockRepository = new MockRepository(MockBehavior.Strict);
}
94. Идеи для применения mock
• Тестирование сложности алгоритмов
• Графический интерфейс
95. Пример: тестирование
пользовательского интерфейса
Puppeteer, Вальтер Антон
(http://blogr.avalter.net/2009/06/puppeteer.html)
• АОП или использование aссessors
• Требует написания кода для обработки элементарных
типов пользовательского интерфейса