SlideShare une entreprise Scribd logo
1  sur  61
Télécharger pour lire hors ligne
50 оттенков красного
Или тестирование без боли
Сергей Александрович, @darth_sim
Немного о себе
Меня зовут Сергей
Я разрабатываю backend у Злых Марсиан
Я люблю писать тесты
Сегодня мы:
Поговорим о том, зачем мы пишем тесты;
Уменьшим объем тестов без потери качества;
Уменьшим время написания и цену тестов;
Упростим поддержку.
Disclaimer
Спорить о тестах можно много и долго. Все сказанное
здесь - мое мнение, основанное на личном опыте
Спонсор многих слайдов
Почему нужно писать
тесты?
Почему нужно писать тесты?
Чтобы беречь свое время при разработке;
Факт от Капитана: автоматические тесты выполняются на
порядок быстрее ручных.
Почему нужно писать тесты?
Чтобы беречь свое время при разработке;
Чтобы не бояться что-то сломать;
Факт от Капитана: Все делают ошибки. Кто не делает
ошибок, тот нагло врет.
Почему нужно писать тесты?
Чтобы беречь свое время при разработке;
Чтобы не бояться что-то сломать;
Чтобы держать архитектуру приложения в форме.
(касается в основном unit-тестов)
Почему нужно писать тесты?
Тесты - это взгляд на код со стороны.
Код сложно тестировать?
⬇
Код сложен, запутан
⬇
Код нуждается в рефакторинге
Почему иногда мы не пишем
тесты?
Потому что часто тесты выглядят вот так:
Пора навести порядок!
Test coverage
Test coverage
Однажды программист спроcил Великого
Мастера: «Какого покрытия тестами я должен
достичь?»
goo.gl/NH84c6
Test coverage
Не дает никакого представления о том, насколько
хорошо протестирован код;
Показывает, какие места точно не протестированы, но
не наоборот;
Не дает никакого представления о качестве кода;
Не та метрика, за которой стоит гнаться.
Test coverage
100% не стоит вашего времени;
90% — это очень хорошее покрытие;
70% вполне достаточно.
Test coverage
“Мне платят за код, который работает, а не за
тесты, поэтому моя философия заключается в
том, чтобы тестировать настолько мало,
насколько это возможно для достижения
нужного уровня уверенности„
(Кент Бек)
Выбрасываем лишнее
Выбрасываем лишнее
Тесты некритичного кода
Если ошибка в коде не повлечет за собой серьезных
последствий, то тестирование этого участка кода совсем
не обязательно.
Выбрасываем лишнее
Тесты поведения сторонних библиотек
Большинство библиотек уже протестированы
разработчиком;
Если вас не устраивает, как они протестированы, лучше
сделать контрибьют, чем держать тесты у себя.
Выбрасываем лишнее
Косвенно выполненные проверки
Проверка на наличие классов и методов;
Проверка количества аргументов функций;
Проверка на отсутствие исключений.
Иногда такие проверки необходимы, но такие случаи
редки.
Выбрасываем лишнее
Тесты приватных методов
Приватные методы проверяются тестами открытых
методов, в которых они вызываются
Выбрасываем лишнее
Тесты тривиального кода
“Если я не делаю ошибок какого-то рода, я не
тестирую код на их наличие„
(Кент Бек)
Приводим оставшееся в
порядок
Приводим оставшееся в порядок
“Пишите код так, как будто сопровождать
его будет склонный к насилию психопат,
который знает, где вы живёте„
(Мартин Голдинг)
Верно и для тестов.
Используйте говорящие имена
тестов
Тест — это спецификация с функцией самопроверки. По
названию теста должно быть понятно, что конкретно он
проверяет.
Используйте говорящие имена
тестов
Bad:
it 'works' do
# ...
end
Good:
it 'sends email to the user' do
# ...
end
Используйте говорящие имена
тестов
Если фреймворк не позволяет в полной мере описать тест
с помощью имени, напишите комментарий
# Sends email to the user
def test_send_message
# ...
end
Один тест - одна проверка
По выводу тестового фреймворка должно быть понятно,
какие конкретно действия выполняются не так, как
ожидалось.
Один тест - одна проверка
Bad:
it 'creates message and sends it to the user via email' do
# ...
end
Good:
it 'creates message' do
# ...
end
it 'sends message to the user via email' do
# ...
end
Один тест - одна проверка
Аналогично для фреймворков без возможности подробно
описать тест
# Creates message
def test_send_message__message_creation
# ...
end
# Sends message to user
def test_send_message__message_sending
# ...
end
Один тест - одна проверка
Некоторые фреймворки позволяют писать комментарии к
проверкам. В таком случае разделение не так важно.
Пример для testify (go):
// Creates message
func Test_sendMessage(t *testing.T) {
// ...
assert.Equal(t, expected, actual,
"Should create message")
// ...
assert.Equal(t, expected, actual,
"Should send message to user via email")
// ...
}
Используйте контексты
Если фреймворк позволяет задавать контекст
тестирования — пользуйтесь этой возможностью
Используйте контексты
Bad:
it 'creates message when user is signed in' do
sign_in(user)
# ...
end
Good:
context 'when user is signed in' do
before { sign_in(user) }
it 'creates message' do
# ...
end
end
Используйте контексты
Если фреймворк не поддерживает контексты, можно опять
обратиться к комментариям
# = When user is signed in ==============================
# Creates message
def test_send_message__signed_in__message_creation
# ...
end
# = end When user is signed in
Правильные ожидания
Правильно подобраное ожидание - половина написанного
теста.
Правильные ожидания
Примеры хороших ожиданий:
Возвращаемое значение;
Изменения состояния класса, видимого извне;
Внешнее воздействие a.k.a. side effect;
Обращение к сторонним объектам.
Правильные ожидания
Примеры плохих ожиданий:
Изменения внутренних переменных класса;
Изменение состояния хранилища, используемого для
хранения состояния тестируемого объекта;
Вызовы приватных методов.
Правильные ожидания
Bad:
it "puts provided value to redis" do
subject.set("the value")
expect(REDIS.get("the key")).to eq("the value")
end
Good:
it "saves provided value" do
subject.set("the value")
expect(subject.get).to eq("the value")
end
Правильные ожидания
Старайтесь максимально абстрагироваться от реализации
метода и сосредототочиться на результате
Mocks & stubs
Палка о двух концах:
Помогают достичь нужного уровня изоляции;
При злоупотреблении могут сделать тест бесполезным.
Mocks & stubs
Хорошие кандидаты:
Передаваемые на вход объекты;
Сетевые службы;
Функции с трудно прогнозируемым или трудно
выводимым результатом, используемые в тестируемом
методе.
Mocks & stubs
Нужно очень осторожно подходить к стабу БД.
Если не уверены на 100%, не делайте этого
Работа с внешними связями
Ситуация №1:
Функция foo объекта A (A.foo) проводит вычисления со
сложной логикой, основываясь на результатах функции
bar объекта B (B.bar);
B.bar в свою очередь тоже проводит вычисления со
сложной логикой.
Необходимо протестировать метод A.foo
Работа с внешними связями
Вариант решения №1:
Написать тест, учитывающий логику функции B.bar.
Нарушение DRY, повторное тестирование B.bar,
тестирование логики, не относящейся к тестируемому
методу
Работа с внешними связями
Вариант решения №2:
Создать условия для получения заранее известного
результата B.bar, использовать этот результат для
тестирования A.foo.
Тест становится зависимым от логики B.bar.
Изменение логики стороннего метода сломает тест.
Работа с внешними связями
Вариант решения №3:
Сделать stub B.bar с известным результатом,
использовать этот результат для тестирования A.foo.
Тест не зависит от логики B.bar
Работа с внешними связями
Проблема варианта №3: Изменение интерфейса
функции B.bar сломает код, но оставит тест ложно
положительным.
Решение: Это тот самый случай, когда чистый прогон
функции с проверкой на отсутствие исключений имеет
место быть.
Работа с внешними связями
Ситуация №2:
Метод foo объекта A (A.foo) проводит вычисления со
сложной логикой и затем вызывает метод bar объекта B
(B.bar);
B.bar в свою очередь тоже реализует сложную логику.
Необходимо протестировать метод A.foo
Работа с внешними связями
Вариант решения №1:
Проверить side effect метода B.bar, учитывая его логику.
Нарушение DRY, повторное тестирование B.bar,
тестирование логики, не относящейся к тестируемому
методу
Работа с внешними связями
Вариант решения №2:
Проверить некоторую неизменную часть side effect'а
метода B.bar, не зависящую от его логики. Пример: mailer
отправляет письмо с неизменным заголовком.
Тест не зависит от логики B.bar. Изменение
интерфейса B.bar будет обнаружено сразу.
Работа с внешними связями
Вариант решения №3:
Сделать stub B.bar, проверить факт его вызова после
выполнения A.foo.
Тест не зависит от логики B.bar
Имеет ту же проблему и аналогичное решение, что и
вариант решения №3 предыдущей ситуации.
Гораздо лучше!
Поддерживаем порядок
Составьте договоренности
Если работаете в команде, составьте styleguide для тестов,
хотя бы на словах. Это существенно снизит порог
вхождения в чужие тесты.
Test first!
“Не доверяй тесту, который ты не видел
упавшим„
(народная мудрость)
Почему test first?
Если строить код на основе тестов, то у вас практически
не возникнет проблем с тестируемостью;
Хорошо организованные тесты позволяют продумать и
описать структуру кода до реализации.
Почему test first?
Главный аргумент
Не зная точной реализации, вы будете вынуждены
тестировать только интерфейс, что и требуется
Test first
Перед написанием тестов, постройте дерево с помощью
контекстов. Постарайтесь отобразить все возможные
варианты развития событий.
Итоги
Не гонитесь за test coverage;
Не будьте параноиком, определите для себя, что нужно
тестировать;
Описывайте тесты так, чтобы другой человек мог понять
тестируемый функционал;
Тестируйте интерфейс, а не реализацию;
Делайте unit-тесты независимыми от функционала
других классов/методов;
Используйте тесты как спецификацию для вашего кода;
Спасибо
Вопросы?

Contenu connexe

Tendances

обработка исключений в Java
обработка исключений в Javaобработка исключений в Java
обработка исключений в Javametaform
 
Тестирование весна 2014 смешанное занятие 2
Тестирование весна 2014 смешанное занятие 2Тестирование весна 2014 смешанное занятие 2
Тестирование весна 2014 смешанное занятие 2Technopark
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокTatyanazaxarova
 
20100321 virtualization igotti_lecture08
20100321 virtualization igotti_lecture0820100321 virtualization igotti_lecture08
20100321 virtualization igotti_lecture08Computer Science Club
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Tatyanazaxarova
 
Автоматическая генерация тестов по комментариям к программному коду
Автоматическая генерация тестов по комментариям к программному кодуАвтоматическая генерация тестов по комментариям к программному коду
Автоматическая генерация тестов по комментариям к программному кодуAlexey Noskov
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)Alexander Gornik
 
Тест-дизайн в тестировании ПО. Задача "Треугольник"
Тест-дизайн в тестировании ПО. Задача "Треугольник"Тест-дизайн в тестировании ПО. Задача "Треугольник"
Тест-дизайн в тестировании ПО. Задача "Треугольник"OdessaQA
 
Статический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeСтатический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeTatyanazaxarova
 
Особенности тестирования открытого ПО
Особенности тестирования открытого ПООсобенности тестирования открытого ПО
Особенности тестирования открытого ПОAlexey Lyanguzov
 
Programmers' Mistakes for Dummies
Programmers' Mistakes for DummiesProgrammers' Mistakes for Dummies
Programmers' Mistakes for DummiesCOTOHA
 
Специфика тестирования проектов с открытым исходным кодом
Специфика тестирования проектов с открытым исходным кодомСпецифика тестирования проектов с открытым исходным кодом
Специфика тестирования проектов с открытым исходным кодомSQALab
 
Делаем Unit тесты проще
Делаем Unit тесты прощеДелаем Unit тесты проще
Делаем Unit тесты прощеSergii Zelenin
 
Инструментальный подход к разработке протоколов
Инструментальный подход к разработке протоколовИнструментальный подход к разработке протоколов
Инструментальный подход к разработке протоколовfurj
 
Как создать качественный статический анализатор
Как создать качественный статический анализаторКак создать качественный статический анализатор
Как создать качественный статический анализаторAndrey Karpov
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROITatyanazaxarova
 
Статический анализ кода: современный взгляд
Статический анализ кода: современный взглядСтатический анализ кода: современный взгляд
Статический анализ кода: современный взглядAndrey Karpov
 

Tendances (20)

обработка исключений в Java
обработка исключений в Javaобработка исключений в Java
обработка исключений в Java
 
Тестирование весна 2014 смешанное занятие 2
Тестирование весна 2014 смешанное занятие 2Тестирование весна 2014 смешанное занятие 2
Тестирование весна 2014 смешанное занятие 2
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
20100321 virtualization igotti_lecture08
20100321 virtualization igotti_lecture0820100321 virtualization igotti_lecture08
20100321 virtualization igotti_lecture08
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
 
Автоматическая генерация тестов по комментариям к программному коду
Автоматическая генерация тестов по комментариям к программному кодуАвтоматическая генерация тестов по комментариям к программному коду
Автоматическая генерация тестов по комментариям к программному коду
 
разработка бизнес приложений (8)
разработка бизнес приложений (8)разработка бизнес приложений (8)
разработка бизнес приложений (8)
 
Наследование и полиморфизм
Наследование и полиморфизмНаследование и полиморфизм
Наследование и полиморфизм
 
Тест-дизайн в тестировании ПО. Задача "Треугольник"
Тест-дизайн в тестировании ПО. Задача "Треугольник"Тест-дизайн в тестировании ПО. Задача "Треугольник"
Тест-дизайн в тестировании ПО. Задача "Треугольник"
 
Статический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMergeСтатический анализ исходного кода на примере WinMerge
Статический анализ исходного кода на примере WinMerge
 
Классы и объекты С#
Классы и объекты С#Классы и объекты С#
Классы и объекты С#
 
Особенности тестирования открытого ПО
Особенности тестирования открытого ПООсобенности тестирования открытого ПО
Особенности тестирования открытого ПО
 
Programmers' Mistakes for Dummies
Programmers' Mistakes for DummiesProgrammers' Mistakes for Dummies
Programmers' Mistakes for Dummies
 
Специфика тестирования проектов с открытым исходным кодом
Специфика тестирования проектов с открытым исходным кодомСпецифика тестирования проектов с открытым исходным кодом
Специфика тестирования проектов с открытым исходным кодом
 
Делаем Unit тесты проще
Делаем Unit тесты прощеДелаем Unit тесты проще
Делаем Unit тесты проще
 
Инструментальный подход к разработке протоколов
Инструментальный подход к разработке протоколовИнструментальный подход к разработке протоколов
Инструментальный подход к разработке протоколов
 
Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practices
 
Как создать качественный статический анализатор
Как создать качественный статический анализаторКак создать качественный статический анализатор
Как создать качественный статический анализатор
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROI
 
Статический анализ кода: современный взгляд
Статический анализ кода: современный взглядСтатический анализ кода: современный взгляд
Статический анализ кода: современный взгляд
 

Similaire à 50 оттенков красного

JavaTalks.Unit Testing.Part 1
JavaTalks.Unit Testing.Part 1JavaTalks.Unit Testing.Part 1
JavaTalks.Unit Testing.Part 1sgdread
 
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...QAFest
 
Ошибки начинающих Tdd практиков, плюсы применения
Ошибки начинающих Tdd практиков, плюсы примененияОшибки начинающих Tdd практиков, плюсы применения
Ошибки начинающих Tdd практиков, плюсы примененияzheldak
 
Автотесты и образ мышления
Автотесты и образ мышленияАвтотесты и образ мышления
Автотесты и образ мышленияAndrei Zubov
 
Собеседование на позицию Java Developer
Собеседование на позицию Java DeveloperСобеседование на позицию Java Developer
Собеседование на позицию Java DeveloperOlexandra Dmytrenko
 
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...HappyDev
 
Документация тестировщика - Александр Трибушный
Документация тестировщика - Александр ТрибушныйДокументация тестировщика - Александр Трибушный
Документация тестировщика - Александр ТрибушныйDataArt
 
Getting Tested: методология интеграционного тестирования
Getting Tested: методология интеграционного тестированияGetting Tested: методология интеграционного тестирования
Getting Tested: методология интеграционного тестированияAlexander Byndyu
 
Continious integration-Automated Testing-Solid-Agile
Continious integration-Automated Testing-Solid-AgileContinious integration-Automated Testing-Solid-Agile
Continious integration-Automated Testing-Solid-AgileKairat Yussupov
 
Повышение качества тестов и автоматическая валидация REST API документации
Повышение качества тестов и автоматическая валидация REST API документацииПовышение качества тестов и автоматическая валидация REST API документации
Повышение качества тестов и автоматическая валидация REST API документацииCEE-SEC(R)
 
60 антипаттернов для С++ программиста
60 антипаттернов для С++ программиста60 антипаттернов для С++ программиста
60 антипаттернов для С++ программистаAndrey Karpov
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать кодVladimir Filonov
 
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Javakranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в JavaKrivoy Rog IT Community
 
XP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeXP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeDmytro Mindra
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать кодMoscowDjango
 
QA Fest 2019. Андрей Солнцев. Десять причин моей ненависти
QA Fest 2019. Андрей Солнцев. Десять причин моей ненавистиQA Fest 2019. Андрей Солнцев. Десять причин моей ненависти
QA Fest 2019. Андрей Солнцев. Десять причин моей ненавистиQAFest
 

Similaire à 50 оттенков красного (20)

JavaTalks.Unit Testing.Part 1
JavaTalks.Unit Testing.Part 1JavaTalks.Unit Testing.Part 1
JavaTalks.Unit Testing.Part 1
 
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...
QA Fest 2014. Алексей Лупан. Не тест-кейсы красят тестировщика, а...
 
Ошибки начинающих Tdd практиков, плюсы применения
Ошибки начинающих Tdd практиков, плюсы примененияОшибки начинающих Tdd практиков, плюсы применения
Ошибки начинающих Tdd практиков, плюсы применения
 
Автотесты и образ мышления
Автотесты и образ мышленияАвтотесты и образ мышления
Автотесты и образ мышления
 
Собеседование на позицию Java Developer
Собеседование на позицию Java DeveloperСобеседование на позицию Java Developer
Собеседование на позицию Java Developer
 
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...
Виталий Шибаев - Креативный менеджмент глазами разработчика: как выжить в agi...
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
Документация тестировщика - Александр Трибушный
Документация тестировщика - Александр ТрибушныйДокументация тестировщика - Александр Трибушный
Документация тестировщика - Александр Трибушный
 
Getting Tested: методология интеграционного тестирования
Getting Tested: методология интеграционного тестированияGetting Tested: методология интеграционного тестирования
Getting Tested: методология интеграционного тестирования
 
Continious integration-Automated Testing-Solid-Agile
Continious integration-Automated Testing-Solid-AgileContinious integration-Automated Testing-Solid-Agile
Continious integration-Automated Testing-Solid-Agile
 
презентация планов
презентация плановпрезентация планов
презентация планов
 
презентация планов
презентация плановпрезентация планов
презентация планов
 
UI+unit testing in iOS
UI+unit testing in iOSUI+unit testing in iOS
UI+unit testing in iOS
 
Повышение качества тестов и автоматическая валидация REST API документации
Повышение качества тестов и автоматическая валидация REST API документацииПовышение качества тестов и автоматическая валидация REST API документации
Повышение качества тестов и автоматическая валидация REST API документации
 
60 антипаттернов для С++ программиста
60 антипаттернов для С++ программиста60 антипаттернов для С++ программиста
60 антипаттернов для С++ программиста
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать код
 
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Javakranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
 
XP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeXP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy code
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать код
 
QA Fest 2019. Андрей Солнцев. Десять причин моей ненависти
QA Fest 2019. Андрей Солнцев. Десять причин моей ненавистиQA Fest 2019. Андрей Солнцев. Десять причин моей ненависти
QA Fest 2019. Андрей Солнцев. Десять причин моей ненависти
 

50 оттенков красного

  • 1. 50 оттенков красного Или тестирование без боли Сергей Александрович, @darth_sim
  • 2. Немного о себе Меня зовут Сергей Я разрабатываю backend у Злых Марсиан Я люблю писать тесты
  • 3. Сегодня мы: Поговорим о том, зачем мы пишем тесты; Уменьшим объем тестов без потери качества; Уменьшим время написания и цену тестов; Упростим поддержку.
  • 4. Disclaimer Спорить о тестах можно много и долго. Все сказанное здесь - мое мнение, основанное на личном опыте
  • 7. Почему нужно писать тесты? Чтобы беречь свое время при разработке; Факт от Капитана: автоматические тесты выполняются на порядок быстрее ручных.
  • 8. Почему нужно писать тесты? Чтобы беречь свое время при разработке; Чтобы не бояться что-то сломать; Факт от Капитана: Все делают ошибки. Кто не делает ошибок, тот нагло врет.
  • 9. Почему нужно писать тесты? Чтобы беречь свое время при разработке; Чтобы не бояться что-то сломать; Чтобы держать архитектуру приложения в форме. (касается в основном unit-тестов)
  • 10. Почему нужно писать тесты? Тесты - это взгляд на код со стороны. Код сложно тестировать? ⬇ Код сложен, запутан ⬇ Код нуждается в рефакторинге
  • 11. Почему иногда мы не пишем тесты? Потому что часто тесты выглядят вот так:
  • 14. Test coverage Однажды программист спроcил Великого Мастера: «Какого покрытия тестами я должен достичь?» goo.gl/NH84c6
  • 15. Test coverage Не дает никакого представления о том, насколько хорошо протестирован код; Показывает, какие места точно не протестированы, но не наоборот; Не дает никакого представления о качестве кода; Не та метрика, за которой стоит гнаться.
  • 16. Test coverage 100% не стоит вашего времени; 90% — это очень хорошее покрытие; 70% вполне достаточно.
  • 17. Test coverage “Мне платят за код, который работает, а не за тесты, поэтому моя философия заключается в том, чтобы тестировать настолько мало, насколько это возможно для достижения нужного уровня уверенности„ (Кент Бек)
  • 19. Выбрасываем лишнее Тесты некритичного кода Если ошибка в коде не повлечет за собой серьезных последствий, то тестирование этого участка кода совсем не обязательно.
  • 20. Выбрасываем лишнее Тесты поведения сторонних библиотек Большинство библиотек уже протестированы разработчиком; Если вас не устраивает, как они протестированы, лучше сделать контрибьют, чем держать тесты у себя.
  • 21. Выбрасываем лишнее Косвенно выполненные проверки Проверка на наличие классов и методов; Проверка количества аргументов функций; Проверка на отсутствие исключений. Иногда такие проверки необходимы, но такие случаи редки.
  • 22. Выбрасываем лишнее Тесты приватных методов Приватные методы проверяются тестами открытых методов, в которых они вызываются
  • 23. Выбрасываем лишнее Тесты тривиального кода “Если я не делаю ошибок какого-то рода, я не тестирую код на их наличие„ (Кент Бек)
  • 25. Приводим оставшееся в порядок “Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте„ (Мартин Голдинг) Верно и для тестов.
  • 26. Используйте говорящие имена тестов Тест — это спецификация с функцией самопроверки. По названию теста должно быть понятно, что конкретно он проверяет.
  • 27. Используйте говорящие имена тестов Bad: it 'works' do # ... end Good: it 'sends email to the user' do # ... end
  • 28. Используйте говорящие имена тестов Если фреймворк не позволяет в полной мере описать тест с помощью имени, напишите комментарий # Sends email to the user def test_send_message # ... end
  • 29. Один тест - одна проверка По выводу тестового фреймворка должно быть понятно, какие конкретно действия выполняются не так, как ожидалось.
  • 30. Один тест - одна проверка Bad: it 'creates message and sends it to the user via email' do # ... end Good: it 'creates message' do # ... end it 'sends message to the user via email' do # ... end
  • 31. Один тест - одна проверка Аналогично для фреймворков без возможности подробно описать тест # Creates message def test_send_message__message_creation # ... end # Sends message to user def test_send_message__message_sending # ... end
  • 32. Один тест - одна проверка Некоторые фреймворки позволяют писать комментарии к проверкам. В таком случае разделение не так важно. Пример для testify (go): // Creates message func Test_sendMessage(t *testing.T) { // ... assert.Equal(t, expected, actual, "Should create message") // ... assert.Equal(t, expected, actual, "Should send message to user via email") // ... }
  • 33. Используйте контексты Если фреймворк позволяет задавать контекст тестирования — пользуйтесь этой возможностью
  • 34. Используйте контексты Bad: it 'creates message when user is signed in' do sign_in(user) # ... end Good: context 'when user is signed in' do before { sign_in(user) } it 'creates message' do # ... end end
  • 35. Используйте контексты Если фреймворк не поддерживает контексты, можно опять обратиться к комментариям # = When user is signed in ============================== # Creates message def test_send_message__signed_in__message_creation # ... end # = end When user is signed in
  • 36. Правильные ожидания Правильно подобраное ожидание - половина написанного теста.
  • 37. Правильные ожидания Примеры хороших ожиданий: Возвращаемое значение; Изменения состояния класса, видимого извне; Внешнее воздействие a.k.a. side effect; Обращение к сторонним объектам.
  • 38. Правильные ожидания Примеры плохих ожиданий: Изменения внутренних переменных класса; Изменение состояния хранилища, используемого для хранения состояния тестируемого объекта; Вызовы приватных методов.
  • 39. Правильные ожидания Bad: it "puts provided value to redis" do subject.set("the value") expect(REDIS.get("the key")).to eq("the value") end Good: it "saves provided value" do subject.set("the value") expect(subject.get).to eq("the value") end
  • 40. Правильные ожидания Старайтесь максимально абстрагироваться от реализации метода и сосредототочиться на результате
  • 41. Mocks & stubs Палка о двух концах: Помогают достичь нужного уровня изоляции; При злоупотреблении могут сделать тест бесполезным.
  • 42. Mocks & stubs Хорошие кандидаты: Передаваемые на вход объекты; Сетевые службы; Функции с трудно прогнозируемым или трудно выводимым результатом, используемые в тестируемом методе.
  • 43. Mocks & stubs Нужно очень осторожно подходить к стабу БД. Если не уверены на 100%, не делайте этого
  • 44. Работа с внешними связями Ситуация №1: Функция foo объекта A (A.foo) проводит вычисления со сложной логикой, основываясь на результатах функции bar объекта B (B.bar); B.bar в свою очередь тоже проводит вычисления со сложной логикой. Необходимо протестировать метод A.foo
  • 45. Работа с внешними связями Вариант решения №1: Написать тест, учитывающий логику функции B.bar. Нарушение DRY, повторное тестирование B.bar, тестирование логики, не относящейся к тестируемому методу
  • 46. Работа с внешними связями Вариант решения №2: Создать условия для получения заранее известного результата B.bar, использовать этот результат для тестирования A.foo. Тест становится зависимым от логики B.bar. Изменение логики стороннего метода сломает тест.
  • 47. Работа с внешними связями Вариант решения №3: Сделать stub B.bar с известным результатом, использовать этот результат для тестирования A.foo. Тест не зависит от логики B.bar
  • 48. Работа с внешними связями Проблема варианта №3: Изменение интерфейса функции B.bar сломает код, но оставит тест ложно положительным. Решение: Это тот самый случай, когда чистый прогон функции с проверкой на отсутствие исключений имеет место быть.
  • 49. Работа с внешними связями Ситуация №2: Метод foo объекта A (A.foo) проводит вычисления со сложной логикой и затем вызывает метод bar объекта B (B.bar); B.bar в свою очередь тоже реализует сложную логику. Необходимо протестировать метод A.foo
  • 50. Работа с внешними связями Вариант решения №1: Проверить side effect метода B.bar, учитывая его логику. Нарушение DRY, повторное тестирование B.bar, тестирование логики, не относящейся к тестируемому методу
  • 51. Работа с внешними связями Вариант решения №2: Проверить некоторую неизменную часть side effect'а метода B.bar, не зависящую от его логики. Пример: mailer отправляет письмо с неизменным заголовком. Тест не зависит от логики B.bar. Изменение интерфейса B.bar будет обнаружено сразу.
  • 52. Работа с внешними связями Вариант решения №3: Сделать stub B.bar, проверить факт его вызова после выполнения A.foo. Тест не зависит от логики B.bar Имеет ту же проблему и аналогичное решение, что и вариант решения №3 предыдущей ситуации.
  • 55. Составьте договоренности Если работаете в команде, составьте styleguide для тестов, хотя бы на словах. Это существенно снизит порог вхождения в чужие тесты.
  • 56. Test first! “Не доверяй тесту, который ты не видел упавшим„ (народная мудрость)
  • 57. Почему test first? Если строить код на основе тестов, то у вас практически не возникнет проблем с тестируемостью; Хорошо организованные тесты позволяют продумать и описать структуру кода до реализации.
  • 58. Почему test first? Главный аргумент Не зная точной реализации, вы будете вынуждены тестировать только интерфейс, что и требуется
  • 59. Test first Перед написанием тестов, постройте дерево с помощью контекстов. Постарайтесь отобразить все возможные варианты развития событий.
  • 60. Итоги Не гонитесь за test coverage; Не будьте параноиком, определите для себя, что нужно тестировать; Описывайте тесты так, чтобы другой человек мог понять тестируемый функционал; Тестируйте интерфейс, а не реализацию; Делайте unit-тесты независимыми от функционала других классов/методов; Используйте тесты как спецификацию для вашего кода;