Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
PyCon Russia 2018 — Unit Testing
Search
Vadim Pushtaev
July 23, 2018
Programming
440
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
PyCon Russia 2018 — Unit Testing
Vadim Pushtaev
July 23, 2018
More Decks by Vadim Pushtaev
See All by Vadim Pushtaev
Perl course (5), testing
vadimpushtaev
0
130
MR::Go::Admin::Test
vadimpushtaev
0
98
Perl course (4), testing
vadimpushtaev
0
130
Perl course (4), db
vadimpushtaev
0
240
Perl course (4), oop
vadimpushtaev
1
200
Perl course (4), mod
vadimpushtaev
0
210
Perl course (3), testing
vadimpushtaev
0
110
Perl course (3), db
vadimpushtaev
0
97
Perl course (3), oop
vadimpushtaev
0
120
Other Decks in Programming
See All in Programming
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
390
速習iPhone Duo対応
yuukiw00w
1
710
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
190
The Rails Doctrine Decade
koic
2
310
世界の中心で、AI(App Intents)をさけぶ ー App Intents中心設計の実践ガイド
touyou
0
650
App Storeの外へ──日本のiOSサイドローディング入門 for iOSDC Japan 2026
yuukiw00w
0
230
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
350
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
1
380
App Intentsのビルドプロセスを支える技術
kntkymt
0
420
Verilogで学ぶCPU自作入門.pdf
uyuki234
2
830
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
0
1.7k
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
4.9k
Featured
See All Featured
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
820
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
500
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
260
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
560
Writing Fast Ruby
sferik
630
63k
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
700
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
360
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
2
2.9k
Transcript
UNIT TESTING Вадим Пуштаев
Принципы Реализация 02/34 Что мы хотим? План доклада
03/34 Что мы хотим?
Регрессия 04/34
Влияние на архитектуру 05/34
Понимание 06/34
Отладка 07/34
Комфорт 08/34
Принципы
f: X Y Test the interface, not the implemen- tation?
X Y 10/34
Test the interface, not the implemen- tation? Y f: X
Y X 11/34
Test first 12/34
Unit testing ≠ testing 13/34
14/34 unittest xUnit – SUnit – JUnit – RUnit →
RSpec – ...
unittest Meszaros, Gerard (2007) xUnit Test Patterns 15/34
Этапы теста Arrange Act Assert Setup Exercise Verify Teardown 16/34
Setup Память Файловая система База данных … 17/34
Гибкие параметры def create(dt=None): if dt is None: dt =
datetime.now() def download(requests_lib=None): if requests_lib is None: requests_lib = requests class Downloader: def __init__(self, config, logger): self._config = config self._logger = logger 18/34
19/34 mock from unittest import mock requests_lib = mock.MagicMock() requests_lib.get.return_value.status_code
= 404 @mock.patch('that.module.requests') def test_download(self, requests_lib): requests_lib.get.return_value.status_code = 404
20/34 Файловая система import tempfile� with tempfile.TemporaryFile() as f: f.write(b'Test
data\n') output = io.StringIO() output.write('Test data\n')
База данных Поддельная база 21/34
База данных Поддельная база SQLite 22/34
База данных Поддельная база SQLite Настоящая база 23/34
Данные в базе Копия реальной базы 24/34
Данные в базе Копия реальной базы Слепок реальной базы 25/34
Данные в базе Копия реальной базы Слепок реальной базы Вручную
подготовленные данные 26/34
Данные в базе Копия реальной базы Слепок реальной базы Вручную
подготовленные данные Пустая база 27/34
Данные в базе Копия реальной базы Слепок реальной базы Вручную
подготовленные данные Пустая база Фабрики 28/34
factory_boy import factory from . import models class UserFactory(factory.django.DjangoModelFactory): class
Meta: model = models.User first_name = factory.Sequence( lambda n: 'User #{}'.format(n) ) group = factory.SubFactory(GroupFactory) 29/34 user = UserFactory()
Транзакции setUp = BEGIN tearDown = ROLLBACK self.break_db() 30/34
def setUp(self): super(BaseTestCase, self).setUp() self._patchers = { 'etcd': patch('common.utils.etcd.Etcd.write'), 'celery':
patch('celery.current_app.send_task'), } self._patchers['etcd'].return_value = None self._patchers['celery'].return_value = True for patcher in self._patchers.values(): patcher.start() � Patchers 31/34
def assertDirExists(self, dir_path): self.assertTrue(os.path.isdir(dir_path)) def assertNotFileExists(self, file_path): self.assertFalse(os.path.exists(file_path)) def assertFileContent(self,
content, path): with open(path, 'rb') as fh: self.assertEqual(content, fh.read()) Custom asserts 32/34
@BaseTestCase.cases( ( 'dst', pytz.timezone('Europe/Moscow'), DT.utc(2011, 3, 27, 6, 12, 23),
# 10:12:23 (MSD) datetime.date(2011, 3, 27), ), ( 'no_dst', pytz.timezone('Europe/Moscow'), DT.utc(2016, 2, 7, 23, 43, 12), # 02:43:12 (MSK) datetime.date(2016, 2, 8), ), ) def test_date_of_time(self, timezone, dt, date): self.assertEqual(DT.date_of_time(dt, timezone), date) 33/34 Таблицы результатов https://bit.ly/2NvZdUT
Итого
Спасибо! @pushtaev @pythonetc Пуштаев Вадим
Тестирование приватного метода?
def count_german_letters(self): return len([ x for x in self._string if
self._is_german_letter(x) ]) def delete_german_letters(self): return ''.join( x for x in self._string if not self._is_german_letter(x) ) def _is_german_letter(self, c): return c in string.ascii_lowercase or c in 'ÄäÖöÜü ß'