![](https://images.logmi.jp/media/article/331348/images/main_image_428f593386a1100fe4799e824be362fcd0ef3ab1.jpg?w=600)
2025.02.03
創業125年のロート製薬が、新たな「気づき」を見出し続けられるわけ トヨタのカイゼンにならった「改鮮活動」の実践法
リンクをコピー
記事をブックマーク
Atsushi Odagiri氏(以下、Odagiri):では次にpytestです。pytestもけっこう有名になってきたツールです。flake8とかmypyは静的チェックのツールなので、あいつらは実行させない、つまりコードの内容として「何か危ないぞ」とか「型違うよ」というのをチェックしてくれるツールです。pytestは結局のところユニットテストツールなので、テストで実際にコードを動かしてエラーが出るかというのをやりますが、もちろんみなさんユニットテストはやっていらっしゃいますよね。挙手。
(会場挙手)
はい。pytestを使っている人。
(会場挙手)
両方まだ混ざっている。そんなもんですよね。まずpytestはどういうところがツールとして優秀かというと、エラーが起こった時に、そのエラーの周辺の変数の内容まで表示してくれるという詳細なテストレポートがあります。詳細すぎるので、たくさんエラーが出るとがんばってスクロールしないといけなくなっちゃうんですが。
あとユニットテストとの違いでいえば、アドオンがあること。そしてfixtureの仕組みです。unittestでは、用意をするsetUpと片付けをするtearDownという2つのメソッドがバラけています。pytestではコンテキストマネージャの仕組みでfixtureをするので、yieldを挟むんです。準備してyieldして、そうするとテストが終わったあとにまたこのyieldのところに戻ってくるので、ここで後片付けをする。準備と後片付けを一緒にしたfixtureというのをちゃんと書けるんです。
たまに「setUpで呼ぶけど、こいつはtearDownで呼ぶ必要はないのか?」みたいなやつとかありますよね。そういうのが2つセットになってfixtureとして使えるというのがpytestのfixtureのいいところです。
あとは、始めようとするとTestCaseクラスを継承してテストメソッドの中でassert〜というメソッドを使ってチェックをするというのがunittestなんですけど、そこもういいよねと。test〜という関数だったらもうテストだし、その中でassertすれば詳細なテストレポートの中で変数の中身がわかるので、いちいちassert〜というのをいろいろ使い分けなくてもいいでしょうと。これが、pytest側のいいところだという話です。
unittestはunittestで良いところはあるのですが、今日はpytestの話なので、pytestだけお話します。
Pytestのインストールは、「pip install pytest」です。
昔はpyというライブラリの中に入っているtestというライブラリだったんですけど、みんなpytestしか使わないので分離されているわけです。pytestはアドオンが使えます。どんなアドオンがあるかというと、まずpytest-cov、coverage収集。ユニットテストしたらみなさんcoverageは取りますよね。取っていないユニットテストを実行している人?
(挙手なし)
さすがにいないね。あとは、アドオンですとpytest-djangoがありまして、いろいろできるやつです。pytest-mockは、ユニットテストのモックが中にあります。fixtureとしてパッチをしたものをちゃんときれいにしてくれるとか、結局一度も呼ばれていないモックについて警告してくれたりするので、使うといいと思います。
あとpytest-freezegunは時間関係です。mock datetimeするためのものですけど、これについてのpytest fixtureで使うことができます。いろんなアドオンがあるので調べてみるといいでしょう。どうせそのうち手作りすることになりますがね(笑)。
今日おすすめしたいpytestのアドオンは、pytest-randomlyです。これが何をするかというと、random seedを変更するのがまず1つ。そしてもう1つ、一番重要なのが、テスト順序をシャッフルするということです。
さあ、なぜテスト順序をシャッフルする必要があるのでしょうか? 例えばテスト対象で、このように固定のFILENAMEという定数の中に、何か受け取った内容をJSONで書き込むやつとJSONを読み込むやつがあります。すごいアホなことをやっているんですけど、そこは目を瞑ってください。
ではこれのテストを書きましょう。テストを書いちゃいました。まずtest_saveのほうはsaveメソッドを呼んで実際そのファイルの中身が正しいかを見るものですね。そしてtest_loadのほうですが、loadを呼んだresultの中身が書き込まれているというアホなことをやっていますが……。
これは非常に危険なテストと言いますか、「何をやっているの?」というテストなんですが、それでもユニットテストをやり始めるとこんなコードもたくさん出てきます。
まず何が問題かというと、test_loadはtest_saveの実行後じゃないと動かないんです。いきなり呼んでも動かないんですね。でもこのテストを実行すると、test_saveを呼んでtest_loadを実行するという、この順番で呼ばれてしまうので、うまくいってしまうわけです。
でも実際にはtest_saveを単独実行すると落ちてしまう。だけど、CI上ってわざわざ単独実行なんてしないので、全部実行するわけです。そうすると落ちないんですね。ということは、「じゃあこれはいつも全部テストを実行しないといけないのか?」という話になってしまう。対策として、テスト実行順序をシャッフルすることによって、順序依存のテストを発見することができる。
さっきのテストを書いたやつです。pytest-randomlyが入っていない状態で「pytest .\test_loader.py」とやって、普通に実行すると、test_save→test_loadという順番で実行させるのでうまくいってしまう。
ではここでテストを指定します。pytestがテストを1個実行するための書き方はこうなります。コロンでつないで関数を書いてください。これでちゃんとテストを実行できます。test_loaderだけ実行しようと思ったら、「pytest .\test_loader.py::test_load」となります。
ただ、今これを実行してみると実際にうまく動いてしまいます。さっきテストを実行したので、ちゃんとファイルが存在するので動いてしまうんですね。「なんで俺がfixtureの代わりをしなきゃいけないんだ」という憤りとともに、ちゃんと消してからやってみてください。そうするとエラーになるわけですね。
pytest-randomlyを入れましょう。「$ pip install pytest-randomly」とすると、pytestを実行したら自動でrandomlyがかかります。
ここでシャッフルされるのでたまにうまくいくわけですね。今回の場合2つテスト関数があるので、2分の1の確率でうまくいく。だから3回か4回ぐらいやって、だいたいうまくいけば「とりあえずいいんじゃないかな?」という感じなので、テスト順がシャッフルされるのを複数回実行して、ちゃんとうまくいったりうまくいかなかったりというのを確認しましょう。
次はtoxです。先ほどのpytestはテストランナーでしたが、toxは何かというと、テスト用の環境をいい感じで作ってくれるツールです。toxを使うとテスト用にvirtualenvを作ってその中でテストをするということができます。
あとその中でどういうテストを実行するかも設定で書いておけるので、これをつかってまずvirtualenvを切っていくので、それぞれバージョンの違うPythonのテストをするというのがまず簡単にできるようになります。プロダクションコードではそんなに使うことはないとは思いますが、ライブラリ作成者はだいたいtoxを使って、サポート対象のPythonバージョンを明確にするということをします。
あと、CI上でもtoxを一発呼ぶだけと設定しておくと、CI上でやっているテストをローカルでもすぐにtox一発で実行できます。CI上でしかできない複雑な手順を踏んだテストみたいなやつで落ちて、「ローカルで再現しねぇ」みたいなことになると「もう! あー!」となるわけですよ。そういうことがないようにtoxを使ってこのテストフローをきれいにしておくとまた効率アップできます。
toxをインストールする方法は「pip install tox」です。このへんのツールでpip installがうまくいかないようなツールはほとんどありませんので、安心してみなさん使ってくださいね。
tox.iniでどういうことを書くかというと、toxセクションでテストしたいバージョンを書いておきます。ここでpy36と37と書いてあるので、Python3.6と3.7に対するテストを、それぞれのバージョンのvirtual envを作って、その中でpytestをインストールしてあげて、pytestコマンドを実行すると。
設定ファイルがあって、ここでtoxと打ってあげると、3.6用のvirtual envを作って、その中にpytestを入れて、あと……あっ、skipsdistと書くの忘れた……。まあ、いいや(笑)。あと今開発中のパッケージもそこにインストールして、pytestコマンドを実行するというのを、Python3.6と3.7両方に対して実行してくれます。これが、手元でもtoxと打てばいいし、CI上でもtox一発でこのテストを実行することができる。
「手元に3.6ないので……」というときは、-eオプションを使ってenvを指定する。Python3.7だけ実行したければ、tox -e py37とやれば、その環境だけの実行になります。
以上、こういった効率化をするためのツールがありますが、こういったツールはまずCIで実行しないとみんなちゃんと守らないので、CIで実行します。これはレビュー前の最後の水際なので、ここでは必ず全部通しましょう。
でもCIに言われてからまた直すというと、「commitしてpushして何か実行されるのを待って……」みたいな感じで。最近CIもタスクが流れてくるのが遅いですから、それを待って「あっ、通った」とやるのは遅いです。
なのでもう一歩手前でいきましょう。そうすると、pre-commitです。commitフックもしくはpushのフックで走らせるというのがあります。
ただし、そこでもけっこうだるいです。そしたら、まずcommitする前にコマンドラインで手動実行しましょう。でも手動実行はそのうち忘れます。なので、エディタ上で自動で実行してくれて、編集したら即見えるというような感じにしましょう。
これでエディタからツールを使えるようにする。どういうときにするかというと、編集や保存と同時に警告したりフォーマッティングしてもらうようにしておけば、もう忘れないし、その場で直せる。「このcという変数使ってなくない?」ってピッと出てくれば「あっ!」と気づいてすぐ直せますよね。
あと、自分で実行して、たまに忘れてCIで落ちてがっかりというのはありますね。しかもCIけっこう遅いのに。しかもSlackでぴょーんと「落ちたよ」って感じで飛んできて、「あー」ってみんなの前で赤っ恥かくみたいな感じになるので。「お前、flake8落ちたぜ?」みたいな。そういうことがなくなります。エディタにツールを設定しましょう。
みなさんが使っているエディタはたぶんVS Code、emacs、Vim……。PyCharmはエディタかどうかわからないけど、そのぐらいかな? ほかのエディタを使っている人は信念があってやっていると思うので、がんばってちゃんと設定してあげてください。
エディタでどういう設定をしておくのが良いかというと、まず編集中にflake8やmypyの警告がバンバン出て直せるようにする。あと、保存したらすぐblackでフォーマットされるようにする。あとはテスト実行をエディタの中でやるとエラーメッセージが出ると思うんですけど、そこからすぐにそのエラーの箇所に飛べる機能も設定しておくとよいです。
あと、テストでカバーされた行がマーカーで表示されるというのまでいけるとすごくいいです。「あ、ここテスト通ってない」というのがすぐわかります。
VS codeでそれをやろうとすると、linting.flake8.enabled、mypy.enabled。……pylintはだいたいお役御免なのでenabled false、インストールしたらすぐにやる設定です。python.format.provider = “black”にして、あとformatOnSaveというのをやると、保存時にフォーマットが上がります。
次にemacsの場合は、静的チェックのフレームワークでflycheckとかflymakeがあります。そこを通して「flake8を使う」というのを入れます。flycheck-mypyとかもありますし、今後LSPのほうに寄っていくと思いますので、そこらへんはみなさんで調べてください。「emacsのLSP、まだちょっとなぁ……」とも思うので、まだ伝統的なsaveフックを使った設定方法がいいと思います。
Vim。使ってないのでわかりませんが、たぶん何かあるでしょう。
PyCharm。使ってないからわかりませんが、だいたいできるでしょう。
というわけで、今日はいろんなツールを説明しました。覚えて帰ってほしいことは、退屈なことは機械がやってくれますので、みなさん機械にガンガンチェックさせましょう。レビュアーの時間を奪わないために、機械を使いましょう。
レビュアーももっと突っ込んだレビューをしたいのに、その前段階のことがあると心がざわついて少し攻撃的になってしまうなんてこともあるわけです。flake8をチェックすればわかるようなことがレビュアーの目の前に来たら、ちょっと怒りたくもなるわけです。
そう言われたほうも直すのがちょっと嫌になって、お互いの精神衛生上良くないですから、事前に機械にチェックさせて、指摘されたところをそのまま直すということをやりましょう。
あとは、自分でやってみてほしいこととして、まだプロジェクトにこういったツールを導入していない場合は、「プロジェクトにこういう効果があるんですよ!」と導入してみましょう。CIを回しているプロジェクトであればCIに入れてみましょう。
というわけで、これでビギナーセッションの、開発を効率化するツール設定のお話を終わります。
(会場拍手)
司会者:ありがとうございました。せっかくの機会ですのでご質問ある方。はい。
質問者1:発表ありがとうございます。flake8とかblackとかmypyでチェックをかけるとき、tox.iniだとかsetup.cfgとかで書いていると思うんですが、それってflake8コマンドを一発で書くのか、toxでenvironを指定してflake8とかblackみたいなかたちで個別に指定する方法がいいのか。
例えばblackとmypyとflake8を一括でチェックするような設定がいいのか。flake8ならflake8であるとを一発でわかるようにしたほうがいいとか、こんなのがおすすめとかありますでしょうか。
Odagiri:toxのタスクをどう構成するかという話?
質問者1:はい。
Odagiri:大体やることは変わらないので、lintingとかそんな感じのタスクを作って、その中でもうflake8、mypy、blackみたいな感じでいっぺんに動かしちゃってますね。
質問者1:なるほど。ありがとうございます。
司会者:ほかにご質問のある方?
Odagiri:ないですかね。
司会者:はい。じゃあご質問がなければ終わりに。
Odagiri:いいですかね。一応今日PSFブースの周りをうろうろしているときであれば質問を受け付けますので、そこらへんにいたら質問してみてください。
司会者:すみません。運営関係の質問ですけど、この資料はのちほど「#pyconjp」タグでTwitterに公開されますか?
Odagiri:例年どおり。
司会者:例年どおりしていただけるということで。ありがとうございます。
Odagiri:SlideshareのアップとURLをconnpassのに上げておきます。
司会者:ありがとうございます。connpassのほうに資料のURLが上がるそうですので、復習などをしていただけたらいいんじゃないかなと思います。
とくに質問等ないようでしたら、これでビギナーセッションを終わりたいと思います。Odagiriさん、どうもありがとうございました。
(会場拍手)
関連タグ:
2025.02.06
すかいらーく創業者が、社長を辞めて75歳で再起業したわけ “あえて長居させるコーヒー店”の経営に込めるこだわり
PR | 2025.02.07
プロジェクトマネージャーは「無理ゲーを攻略するプレイヤー」 仕事を任せられない管理職のためのマネジメントの秘訣
2025.02.06
落合陽一氏や松尾豊氏の研究は社会に届いているか? ひろゆき氏が語るアカデミアの課題と展望
2025.02.05
「納得しないと動けない部下」を変える3つのステップとは マネージャーの悩みを解消する会話のテクニック
2025.01.07
1月から始めたい「日記」を書く習慣 ビジネスパーソンにおすすめな3つの理由
2025.02.10
A4用紙を持ち歩いて殴り書きでアウトプット コクヨのワークスタイルコンサルタントが語る、2種類のメモ術
2025.02.05
エンジニアとして成功するための秘訣とは? ひろゆき氏が語る、自由な働き方を叶えるアプリ開発とキャリア戦略
2025.02.04
日本企業にありがちな「生産性の低さ」の原因 メーカーの「ちょっとした改善」で勝負が決まる仕組みの落とし穴
2025.02.03
「昔は富豪的プログラミングなんてできなかった」 21歳で「2ちゃんねる」を生んだひろゆき氏が語る開発の裏側
PR | 2025.02.04
能登半島地震で自宅は全壊、「これでどうやってDXするねん」 被災したサイボウズ社員と支援者らが語る災害支援のノウハウ
新人の報連相スキルはマネージメントで引きあげろ!~管理職の「他責思考」を排除~
2025.01.29 - 2025.01.29
【手放すTALK LIVE#45】人と組織のポテンシャルが継承されるソース原理 ~人と組織のポテンシャルが花開く「ソース原理」とは~
2024.12.09 - 2024.12.09
『これで採用はうまくいく』著者が語る、今こそ採用担当に届けたい「口説く」力のすべて
2024.11.29 - 2024.11.29
【著者来館】『成果を上げるプレイングマネジャーは「これ」をやらない』出版記念イベント!
2025.01.10 - 2025.01.10
片付けパパ対談【特別編】 整理術×行動術×メモ術で、仕事も人生も自在にデザイン!
2024.12.16 - 2024.12.16