Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
新人の私が_君_修正早いね_と言ってもらえた話.pdf
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
mizzsugar
June 12, 2019
1.3k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
新人の私が_君_修正早いね_と言ってもらえた話.pdf
mizzsugar
June 12, 2019
More Decks by mizzsugar
See All by mizzsugar
厳しさとゆるさの間で迷う人に捧げる個人開発記
mizzsugar
0
60
SQLModel入門〜クエリと型〜
mizzsugar
3
1.5k
フルリモート向いてないと思っていた私が、なんだかんだ健やかに 1年半フルリモート出来ている話
mizzsugar
1
160
Djangoでのプロジェクトだって型ヒントを運用出来る!
mizzsugar
4
9.1k
「動くものは作れる」の一歩先へ 〜「自走プログラマー」の紹介〜
mizzsugar
0
640
pytestの第一歩 〜「テスト駆動Python」の紹介〜
mizzsugar
3
480
データ分析ツール開発でpoetryを使う選択肢
mizzsugar
1
1.2k
unittest.mockを使ってテストを書こう
mizzsugar
5
7k
変数に変数を代入したら?
mizzsugar
1
2.7k
Featured
See All Featured
What's in a price? How to price your products and services
michaelherold
247
13k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Optimizing for Happiness
mojombo
378
71k
Typedesign – Prime Four
hannesfritz
42
3.1k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
19k
Building an army of robots
kneath
306
46k
Amusing Abliteration
ianozsvald
1
200
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
1
340
The Spectacular Lies of Maps
axbom
PRO
1
790
The SEO identity crisis: Don't let AI make you average
varn
0
490
Transcript
職業Pythonista歴半年の私が「君、 修正早いね」 先輩を唸らせた話 2019/06/12 @Stapy mizzsugar0425
お前誰よ ・みずきと申します ・2019/01~ 職業Pythonista(Djangoで受託開発してます。案件 募集中!!!) ・好き→Python/Django/Pyramid/React/PostgreSQL/コーヒー/ ロードバイク/一眼レフ
ある日のこと・・・
上司に仕様変更を伝えられる
それを聞いた先輩「これはエグい変更ですね」
しかし30分程で変更を実装した私
先輩「ぎゃふん」
YOU LOSE!! 俺の勝ち! 戦ってないけど
速攻ポイント ①テストコード書いた ②疎結合な実装を試みた
仕事のコード見せるわけにもいかないので FEの問題を参考にしました 仕様書 https://www.fe-siken.com/kakomon/30_haru/pm05.html 今回書いた詳しいコード(まだ途中) https://github.com/mizzsugar/medical-checkup
健診管理システムでこんな仕様があったとして https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. ①で抽出した従業員を受診対象者とし,健康診断コースを決定する。 3. 受診日を決定し,受診対象者の健康診断レコードを健康管理システ ムに登録する。
突如打ち込まれる新たな手順 https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。
突如打ち込まれる新たな手順 https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。 さあ、 どうする!?!?
①テストコード書いた
テストコード書かないと プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 →エラーになったらエラーの該当箇所を確認 →プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 ・・・
テストコード書かないと プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 →エラーになったらエラーの該当箇所を確認 →プロダクトコードを修正 →ブラウザ開いて画面ぽちぽちして動作確認 ・・・ 大変!!!!(>_<)
テストコード書くと ①「当月が誕生月である従業員を抽出する。」のテストケースのテストのみ書いたテスト がある(すでに通っている) ー当月誕生日である従業員が抽出されている ー当月誕生日でない従業員は抽出されていない ②「前月の定期健康診断において,再検査が必要と判定された従業員を抽出する」のテ ストケースのテストを追加 ー前月の定期健康診断を受けて再検査必要な従業員が抽出されている ー前月の定期健康診断を受けて再検査不要な従業員が抽出されていない ー前月以前に定期健身を受けた従業員は抽出されない
③テスト回す→落ちる→プロダクトコード修正→通る→リファクタリング
①「当月が誕生月である従業員を抽出する。」のテストケースのテストのみ書いたテスト がある(すでに通っている) ー当月誕生日である従業員が抽出されている ー当月誕生日でない従業員は抽出されていない
class TestExtractExamees(TestCase): @classmethod def setUpTestData(cls): """テスト用のデータを作成 長いので省略 """ ... def
test_iter_birthday_month_employees(self): actual = medical_checkup.core.extract_examinee.iter_birthday_month_employees(today=datetime.date(2019, 5, 1)) actual_list = list(actual) with self.subTest('5月が誕生日である社員がリストに入ってる'): self.assertTrue( employee.types.Employee( id=self.emp_1.id, birthday=datetime.date(1990, 5, 10), gender=employee.types.Gender(1), is_manager=False ) and employee.types.Employee( id=self.emp_2.id, birthday=datetime.date(1980, 5, 11), gender=employee.types.Gender(0), is_manager=True ) in actual_list )
) with self.subTest('5月が誕生日でない社員がリストに入っている'): self.assertFalse( employee.types.Employee( id=self.emp_3.id, birthday=datetime.date(1980, 6, 11), gender=employee.types.Gender(0),
is_manager=True ) in actual_list ) self.assertFalse( employee.types.Employee( id=self.emp_4.id, birthday=datetime.date(1980, 6, 12), gender=employee.types.Gender(1), is_manager=True ) in actual_list ) @classmethod def tearDownClass(cls): pass
②「前月の定期健康診断において,再検査が必要と判定された従業員を抽出する」のテ ストケースのテストを追加 ー前月の定期健康診断を受けて再検査必要な従業員が抽出されている ー前月の定期健康診断を受けて再検査不要な従業員が抽出されていない ー前月以前に定期健身を受けた従業員は抽出されない
def test_iter_reexamine_employees(self): # 前月(このテストでは2019年5月)の定期健康診断において,再検査が必要と判定された従業員のみがリストに入っている actual = medical_checkup.core.extract_examinee.iter_reexamine_employees( conducted_year=2019, conducted_month=5 )
actual_list = list(actual) with self.subTest('前月の定期健康診断を受けて再検査必要な従業員が抽出されている'): self.assertTrue( employee.types.Employee( id=self.emp_5.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月の定期健康診断を受けて再検査不要な従業員が抽出されていない'): self.assertTrue( employee.types.Employee( id=self.emp_6.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list
gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月の定期健康診断を受けて再検査不要な従業員が抽出されていない'): self.assertFalse( employee.types.Employee(
id=self.emp_6.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list ) with self.subTest('前月以前に定期健身を受けた従業員は抽出されない'): self.assertFalse( employee.types.Employee( id=self.emp_7.id, birthday=datetime.date(1980,5,11), gender=employee.types.Gender(0), is_manager=True ) in actual_list )
テスト用コマンド打つだけで 結果確認できて楽!
②疎結合な実装を試みた
ここを実装するとします https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. ①で抽出した従業員を受診対象者とし,健康診断コースを決定する。 3. 受診日を決定し,受診対象者の健康診断レコードを健康管理システ ムに登録する。
Viewに全てをまとめるとこうなります。 import datetime from django.http import HttpRespons import employee.models.employee def
show_examiees_fat_controller(request): examinees = [ { 'id': emp.id, 'birthday': emp.birthday } for emp in employee.models.employee.Employee.objects.all() if emp.birthday.month==datetime.date.today().month ] return HttpResponse( { 'examinees': examinees } status=200, content_type='application/json' )
追加されます https://www.fe-siken.com/kakomon/30_haru/pm05.html より 1. 当月が誕生月である従業員を抽出する。 2. 前月の定期健康診断において,再検査が必要と判定された従業員を抽出 する。 3. ①と②で抽出した従業員を受診対象者とし,健康診断コースを決定する。 4.
受診日を決定し,受診対象者の健康診断レコードを健康管理システムに登 録する。
def show_examiees_fat_controller(request): # 当月誕生日の人 employees = [ { 'id': emp.id,
'birthday': emp.birthday } for emp in employee.models.employee.Employee.objects.all() if emp.birthday.month==datetime.date.today().month ] # 前月を定義 last_month = datetime.date.today() - relativedelta(month=1) # 前月の検査で再検査が必要と言われた人 re_exam_employees = [ { 'id': mc.employee.id, 'birthday': mc.employee.birthday } for mc in medical_checkup.models.checkup.MedicalCheckUp.objecsts.all() if mc.conducted_year==last_month.year and mc.conducted_month==last_month.month and mc.need_reexamination ] # 当月誕生日の人と前月の検査で再検査が必要と言われた人の配列を結合 employees += re_exam_employees return HttpResponse(
{ 'employees': employees } status=200, content_type='application/json' ) 動きはするけれども、ひとつの場所で いろんなことやってて大変だなあ・・・
viewと先ほどの計算部分のモジュールを分割すると
# (1)1 当月が誕生月である従業員を抽出する。 def iter_birthday_month_employees(today: datetime.date)->Iterator[employee.types.Employee]: """ today: 基本的にはdatetime.date.today()が入ります """
return ( emp for emp in employee.models.employee.Manager.iter_all() if emp.birthday.month==today.month ) # (1)2 前月の定期健康診断において,再検査が必要と判定された従業員を抽出する。 def iter_reexamine_employees(conducted_year: int, conducted_month: int) -> Iterator[employee.types.Employee]: return ( mc.employee for mc in medical_checkup.models.checkup.Manager.iter_all() if mc.conducted_year==conducted_year and mc.conducted_month==conducted_month and mc.need_reexamination ) # (1)と(2)を合わせたメソッド。View関数内にはこちらをいれる def extract_examiee(today: datetime.date) -> Iterator[employee.types.Employee]: last_month = today - relativedelta(month=1) conducted_year = last_month.year conducted_month = last_month.month return itertools.chain( iter_birthday_month_employees((today)), iter_reexamine_employees(conducted_year, conducted_year) ) service.py
views.py def show_examiees_fat_controller(request): # 対象者を抽出するメソッド employees = [ asdict(emp) for
emp in extract_examiee(datetime.date.today()) ] return HttpResponse( { 'employees': employees } status=200, content_type='application/json' )
Viewで表示する項目が変わった訳ではないので Viewの確認はほとんどしなくて良くなった
まとめ ・テストコードで楽々動作確認 ・疎結合で修正箇所を小さく抑える
ありがとうございました!!!