自社開発プロダクト all-in...

44
単体テストの パフォーマンスチューニング 株式会社 ビジネスバンクグループ 竹中 翔

Upload: shou-takenaka

Post on 10-Apr-2017

3.203 views

Category:

Technology


0 download

TRANSCRIPT

単体テストの パフォーマンスチューニング

株式会社 ビジネスバンクグループ 竹中 翔

• 竹中 翔

• ビジネスバンクグループ CTO

• Twitter/GitHub @shoutakenaka

自己紹介

• 中小企業向けクラウドERPソフト

• 経営を支援する「ビジネス・コックピット」→ 詳しくはこちらの動画にまとめられていますので、是非ご覧ください

• Ruby on Rails

• 2015年7月にファーストリリース

• 鋭意開発中

ALL-IN

• 壮大なコンセプトのシステム

• 設計 → 開発 → リリース → 検証 → 改善 → …

• 自動テスト大事!

ALL-IN

はじめの一歩

そして今

• (テストコードレベルで)単体テストの全体の実行時間を短くするために気を付けていることテストの分散実行とかの話は今日はしません

本日のテーマ

Speed up tests

• rspec

• resque_spec

• factory_girl

• database_cleaner

• faker

主なテスト用gem

• 時間がかかるのは主に外部リソースアクセス(I/O)。

• DB

• File

• Web API

• ALL-IN の場合、外部の Web API をあまり使っていないので、I/O は DB アクセスがメイン。

スピードアップのポイント

• テストケース数が増えてくると、個々のテストのちょっとしたスローダウンが、全体で見ると大きな影響に…

• 0.1 sec * 20000 = 2000 sec = 33 min

スピードアップのポイント

• プロダクションコードで非効率な I/O を発生させないのは当然大切!

• N+1 問題発生してない?

• includes とか eager_load を使う

• 大量のデータをメモリに展開していない?

• find_each、find_in_batches を使う

• kaminari gemとか offset、limit でページングする

スピードアップのポイント

• テストのセットアップも大切!

• 不要なテストデータを DB に入れてない?

• 同じようなデータをテストのたびに毎回DBに入れてない?

→ 適切なタイミングで、必要なデータを投入する!

スピードアップのポイント

不要なデータ問題

• FactoryGirlを使ってます

テストデータの準備

• テスト用のモデルインスタンス+DBへのデータ登録が楽にできる

• デフォルト値でよければcreateするだけ

• テストケースに合わせて、柔軟に特定の属性だけ上書きできる

• create_list(:customer, n) で n個まとめてインスタンス生成+DB登録できる

FactoryGirlを使うと

でもちょっと待った

• モデルのインスタンスさえあれば、DBにデータが入っていなくてもいいテストはある

• モデルのバリデーションのテスト

• モデル → JSON のレンダリングのテスト

• DBアクセスを伴わない計算ロジックのテスト

• etc

• これらのテストケースまで create でインスタンスを作るのは無駄

DB登録は本当に必要?

• DBへの登録は行わず、モデルのインスタンスだけを作る→ INSERTにかかるコストを削減できる!

build を使う

• 関連モデル(アソシエーション)はINSERTされてしまう→ この例だと customer_status はやっぱりDB登録されてしまってる…

buildの罠

• アソシエーションにstrategyをつけるか、build_stubbedでモデルを生成する

関連モデルをDBに入れない

• ブロックを渡すと、必要になったときにブロックの中身を評価して属性値を作ってくれる→ ALL-IN ではこの書き方

遅延評価でもOK

• 他の属性を使って属性値を定義することができる

遅延評価のいいところ

頻繁に登録されるデータ問題

• ALL-IN ではビジネスロジック定義用のクラスには必ずログインユーザーを渡して、「ユーザーがアクセスできる範囲のデータ」に対して処理をしている

例えばログインユーザー

• 何も考えずにこんなコードを書くと、各テストケースのセットアップで毎回 user をDBに登録する処理が走ってしまってコストが高い

例えばログインユーザー

あらかじめDB登録してしまう

• create するときにオプションで渡せば、関連モデルを作成するコストを削減できる

関連モデル作成コストの削減も

• use_transactional_fixturesでテストケースごとにトランザクションを効かせていても、このデータはテスト完了後もDBに残る

• 何がしかの対策が必要

• テスト起動時にDBをクリーンにするとか

• ユーザーIDを固定して、すでにあったら作らないとか

• やりすぎ注意

• セットアップ時に色々登録すると、デフォルトで何が入ってるのか分からなくなる

デメリットもある

テスト分けすぎ問題

• 昔ある人に言われたことがあります「1テストメソッド1Assertionであるべきだ」と。

テストの最小単位とは?

• 本当に1テスト1Assertにすべきか?

テストの最小単位とは?

• 本当に1テスト1Assertにすべきか?

テストの最小単位とは?

• 本当に1テスト1Assertにすべきか?→ 意味的に同じことをテストしているのであれば、そこまで原理主義的にテストを分割しなくていい→ DBアクセスが発生するテストは、テストメソッド数を減らすことでコストが下がる

テストの最小単位とは?

• ある処理をした時、DBに期待通りの数のデータが登録されるか、というテスト。

こういうテストはどうする?

• and で繋げてまとめて確認→ RegistrationService.setup の評価は一回だけ

こういうテストはどうする?

ということで

• 無駄なデータを作らない

• FactoryGirlを上手に使う

• 同じようなデータを登録しない

• テストスィートセットアップ時に登録してしまう

• まとめられるテストケースはまとめる

• 分割しすぎず意味ある単位で1つにまとめる

スピードアップの施策まとめ

で、全テスト回すのに どのくらいかかるの?

どなたか m(_ _)m

Thank you !!