2. Говорим о:
• Что такое тесты?
• Зачем писать тесты?
• Как их писать?
3. Тесты не панацея
• Наличие тестов не означает отсутствие ошибок
• Тесты проверяют ровно то, что в них написано
• Разработка тестов отнимает время
• Тесты нужно поддерживать
• Не верьте в coverage report!
4. Хорошие тесты
• Проверяют что код делает что ожидается
• Не делает ничего кроме того что ожидается
• Полностью покрывают логику
• Взаимо исключают друг друга
• Создают данные в одном месте
5. Проверяют что код делает что ожидается
def remove_friend(user, friend):
user.friends.filter(pk=friend.pk).delete()
…
class UserTests(TestCase):
def setUp(self):
self.user = User.objects.create(id=1)
self.friend1 = User.objects.create(id=2)
self.friend2 = User.objects.create(id=3)
self.user.friends.add(self.friend1)
self.user.friends.add(self.friend2)
def test_user_remove_friend(self):
remove_friend(self.user, self.friend1)
self.assertNotIn(
self.friend1,
self.user.friends.all()
)
6. Код не делает ничего, кроме того что ожидается
def remove_friend(user, friend):
user.friends.all().delete()
…
class UserTests(TestCase):
…
def test_user_remove_friend(self):
friends_count = self.user.friends.count()
remove_friend(self.user, self.friend1)
self.assertNotIn(
self.friend1,
self.user.friends.all()
)
self.assertEqual(self.user.friends.count(),
friends_count - 1)
7. Полностью покрывают логику
Пример из World of Tanks: изменение роли пользователя в клане
А что будет если?
• Пользователь не обладает правами
• Применить новую роль не возможно
• Пользователь пытается сменить роль сам себе
• Пользователь вышел из клана
• …
8. Взаимо исключают друг друга
def test_response_format(self):
result = self.call_api('search', page=0, page_size=2)
expected = [{'id': 1, 'name': 'foo'},
{'id': 2, 'name': 'bar'}]
self.assertEqual(result['things'], expected)
def test_order_by_name(self): # bad
result = self.call_api('search', page=0, page_size=2)
expected = [{'id': 2, 'name': 'bar'},
{'id': 1, 'name': 'foo'}]
self.assertEqual(result['things'], expected)
def test_order_by_name(self): # IMHO good
resp = self.call_api('search', page=0, page_size=2)
ids_order = [item['id'] for item in resp]
self.assertEqual(ids_order, [2, 1])
9. Создают данные в одном месте
def test_actions_history(self):
Action.objects.create(user=self.user, action_type='change_name')
resp = self.call_api('user_actions', user_id=self.user.pk, limit=10)
self.assertResponse(
resp, user_id= user.pk,
actions=[{'id': 1, 'type': 'change_name',
'date': '2014-04-23T16:50:49.849450'}])
def test_actions_history(self):
self.create_history_record(user=self.user, action_type='change_name')
...
10. Хорошие тесты
• Показывают что именно работает не верно
• Проверяют логику работы приложения
• Легко поддерживать
11. Workflow в команде
• Вместе с новой фичей пишутся тесты на нее
• Каждый новый баг воспроизводится тестом
• Тесты запускаются перед каждым коммитом