В докладе я расскажу о том как я вижу и применяю TDD, почему мне это нравится и почему я хочу, чтобы это нравилось другим. Все это на примере какого-нибудь мини-приложения на базе Django.
8. Вектор мышления
Сначала код:
• Разработчик концентрируется на отдельных частях
кода больше чем на общем дизайне
• К моменту написания тестов разработчик уже
устает
• Тесты пишутся уже с учетом особенностей
реализации, в том числе и костылей, если таковые
присутствуют
9. class OneFieldForm(forms.Form):
value = forms.IntegerField()
class SimpleView(View):
def get(self, *args, **kwargs):
form = OneFieldForm()
return render_template(form)
def post(self, *args, **kwargs):
form = OneFieldForm(self.request.POST)
if form.is_valid():
#Какой-то сложный, утомительный код, который
#использует значение из формы
return render_to_response(“success.html")
else:
return render_template(form)
def render_template(self, form):
context = { "form": form,}
return render_to_response("template.html", context)
12. TDD
• При написании тестов, мы не отвлекаемся на
детали реализации
• Более чёткое и целостное представление о
дизайне кода
• Выше скорость написания кода
• Хорошего кода ;)
• Меньше рефакторинга
13. Смена исполнителя
• Проще понять, что уже сделано, а что еще нет –
достаточно запустить тесты
• Тесты как документация – проще разобраться в уже
написанном коде
• Более отчуждаемый код
14. Человечные интерфейсы
• Сразу смотрим на задачу с позиции пользователя
интерфейсов
• Не делаем допущений, основанных на знании
деталей реализации
• Ниже порог вхождения, меньше подводных камней
• Более отчуждаемый код
16. Обычное течение разработки
• Начало. Уровень мотивации высокий, все рвутся в
бой
• Понеслась
– Имплементация
– Передача в QA
– Баги
– Дебагинг
– Мотивация = - 1* Баги
17. TDD
• Тесты пишутся на первой волне энтузиазма
• Для написания кода после тестов появляются
дополнительные стимулы:
– Четко поставленная цель: пройти все тесты
– Каждый пройденный тест – достижение
– Это поддерживает положительный настрой
18. • Стимулы для написания тестов после кода
– Требуется очень хорошая самоорганизация
– Формальных требований может быть недостаточно для
написания хороших тестов
20. • Пишем приёмочные тесты и ставим задачи
команде
• Приёмочные тесты – тесты верхнего уровня
абстракции
• Сами пишем тесты на невалидные входные данные
• Кто, кроме нас? :)
• Сам я ещё не пробовал, но очень хочу
22. • От абстракций верхнего уровня - к абстракциям нижних
– Глобальные вещи (views) -> API, DAO -> утилитарные методы
• Проще показать на примере
Сделаем виртуальную библиотеку.
Книга - название, автор, ISBN
Читатель
Чтобы получить билет, надо зарегистрироваться. Для простоты, в
качестве номера будем использовать django.contrib.auth.models.User.id
Читатели могут брать/сдавать книги, но не более 2х одновременно.
Если книгу кто-то взял, то другому ее не получить.
Брать книги можно по:
• 1. Название+Автор
• 2. ISBN
23. #Книги: title, author, year, isbn
#
#Капитал, Кащей Б.С., 942 г., 1234-5678-9
#100 диетических блюд из репы, Прекрасная В., 1142 г.,
# 4321-8765-9
#Ковка подков, Гефест, 2675 г. до н.э. 9876-5432-1
#Бустâн, Абу Мухаммад Муслих ад-Дин ибн Абд Аллах Саади
# Ширази, 1257 г., 6789-2345-1
class LibraryViewsTestCase(TestCase):
def setUp(self):
self.ivan = User.objects.get(username="ivan")
self.maria = User.objects.get(username="maria")
self.books = INITIAL_BOOKS_LIST
def test_library_urls(self):
self.assertEqual(reverse("library:take"), "/take/")
self.assertEqual(reverse("library:return"), "/return/")
27. def test_get_book_view__title_and_author(self):
"""
Берем книги по заголовку и автору
"""
def test_get_book_view__taken(self):
"""
Попробуем взять книгу, которую уже унес кто-то
"""
def test_get_book_view__again(self):
"""
Попробуем взять одну и ту же книгу дважды
"""
def test_get_book_view__unknown(self):
"""
Попробуем взять книгу, которой нет в библиотеке
"""
#...
#Возврат книги, возврат не взятой книги,
#возврат книги не из библиотеки
29. class LibriatyDaoTestCase(TestCase):
"""
Следующий уровень - функции, которые нам понадобятся,
чтобы решить ситуации, описанные выше
"""
def test_get_book_by_isbn(self):
"""
Получаем их базы книгу по isbn
"""
def test_get_book_by_title_and_author(self):
"""
Получаем из базы книгу по isbn
"""
def test_user_has_book(self):
"""
Есть ли эта книга у читателя
"""
30. def test_get_user_books(self):
"""
Все книги которые есть у читателя
"""
def test_get_user_books_count(self):
"""
Сколько книг у читателя
"""
def test_book_is_owned(self):
"""
Взяли ли кто-то эту книгу
"""
33. • Более продуманный дизайн кода к моменту начала
реализации
• Как следствие - более чистый код
• Возможно, более быстрая реализация
• Лучшее покрытие тестами (как по качеству, так и по
количеству)
• Дополнительный источник мотивации в процессе
• Более отчуждаемый код
• Меньше багов, а значит и меньше итераций «QA-
багфиксинг»