Slide 1

Slide 1 text

テストを 遅くしないように 気をつけること MatchingAgent x Makuake 合同勉強会 2017/06/22 ( 木) @kkkw

Slide 2

Slide 2 text

お前誰よ @kkkw フリーランス 2017 年2 月~ Makuake にjoin サーバーサイド中心に上から下まで

Slide 3

Slide 3 text

Agenda テストが遅いと何が困るのか Makuake でやったテストの高速化 テストコードのリファクタ例

Slide 4

Slide 4 text

テストが遅いと何が困るのか CI が詰まる PR 発行( テスト走る) →マージ→デプロイの流れ 緊急リリースだと致命的 品質が落ちる テストを書く、実行するモチベーションが落ちる 開発手法の選択肢が減ることもある

Slide 5

Slide 5 text

Makuake でやったテストの高速化

Slide 6

Slide 6 text

やったこと 1. MySQL のチューニング 2. テストコードのリファクタ

Slide 7

Slide 7 text

Jenkins のjob 実行時間

Slide 8

Slide 8 text

MySQL のチューニング 環境依存のお話なので今日は割愛

Slide 9

Slide 9 text

テストコードのリファクタ 基本的にはプロダクションコードと 同じことに気をつける 重い処理をやらない データベースのアクセス 外部へのhttp アクセス ディスクI/O テストそのものが必要かどうか考える

Slide 10

Slide 10 text

いくつか紹介 なるべくDB へアクセスしない方法を紹介 php でのサンプル スライドの都合上PSR とかは無視 確認してないのでエラーとかあるかも

Slide 11

Slide 11 text

そのFind 本当に必要? 本当にDB のsetUP が必要かどうか考える 安易に、 nd しない

Slide 12

Slide 12 text

テストしたいコード Class User { public function get_image_path() { return 'img/user/'.$this->id.'/main.jpg'; } }

Slide 13

Slide 13 text

よくあるテスト nd で取ってきてる setUp でDB の初期化が必要になる $user = User::find(1); assertSame('img/user/1/main.jpg' , $user->get_image_path());

Slide 14

Slide 14 text

こっちのが速い DB から検索するのではなく 引数与えてオブジェクト作る $user = User::forge(['id'=>1]); assertSame('img/user/1/main.jpg' , $user->get_image_path());

Slide 15

Slide 15 text

フレームワークに喧嘩を売らない 提供されている機能をテストする必要はない 必要があるのは、ちゃんと設定 or 使えているか 例えば論理削除のテスト

Slide 16

Slide 16 text

users テーブル id name deleted_at 1 山田太郎 null 2 山田花子 2017-06-22 12:00:00

Slide 17

Slide 17 text

よくあるテスト $actual = User::find(1); assertNotEmpty($actual); $actual = User::find(2); assertEmpty($actual);

Slide 18

Slide 18 text

こっちのが速い 例えば、論理削除のクラスを継承していることを 確認するテストにする assertSame('SoftDelete', class_parents(User::class));

Slide 19

Slide 19 text

Mock を使って遅い処理を避ける 時間のかかる処理を毎回にコールする必要はない。 ただし、Mock は使うべきときだけ使うようにする。

Slide 20

Slide 20 text

テストしたいコード AB テスト用に取ってきたユーザーを A グループとB グループに分ける class ServiceUser { public function get_ab_groups(){ $users = User::get_something(); // 時間 SQL User 配列 返 $a = $b = []; foreach($users as $user){ if($user->id % 2 === 0) $a[] = $user; else $b[] = $user; } return [ 'a' => $a, 'b' => $b, ]; } }

Slide 21

Slide 21 text

こうすると早くはできる $users = array_map(function($i){ return User::forge(['id'=> $i]); }, range(1,2)); $mock = Mockery::mock('alias:' . User::class); $mock->shouldReceive('get_something')->andReturn($users); $actual = (new ServiceUser())->get_ab_groups(); assertSomething($actual);

Slide 22

Slide 22 text

TestCase クラスは、2 つに分ける 大抵のテスティングフレームワークは、 TestCase ク ラスのsetUp メソッドでデータの初期化を行うので、 TestCase クラスそのものを分けるようにしておく。 ModelUserTest ModelUserNoDbTest ※ディスクの遅い環境は増やしすぎ注意

Slide 23

Slide 23 text

3 つだとさらによい ModelUserTest データの更新が発生するため毎回初期化する ModelUserReadTest 読み取りのみ。最初だけ初期化する 初期化をsetUp ではなく、setUpBeforeClass 的な とこでやる ModelUserNoDbTest データベースを全く使わない

Slide 24

Slide 24 text

まとめ 遅いテストは悪 テストを走らせる環境のチューニングはする 遅い処理は書かないでテストできないか検討する 最悪Mock に置き換える

Slide 25

Slide 25 text

ご静聴ありがとうございました