読者です 読者をやめる 読者になる 読者になる

Memento memo.

Today I Learned.

Effective Ruby 第六章まとめ テスティング

Ruby EffectiveRuby Test

Effective Ruby 第五章 メタプログラミング まとめ その2 - Memento memo. の続き。

Effective Rubyのテスティング章をまとめていきます。

MiniTestはRuby標準のテスティングライブラリで、 主要なコンポーネントは以下の3つです。

require('minitest/autorun') でライブラリ全体をロードすると、上記のコンポーネントも含まれます。

テストのファイル名は tests/xxx_test.rb といった名前で格納するとrailsの作法に乗れて良いみたいです。

ユニットテストとスペックテスト(ビヘイビアスペック)はどっちでもいいらしいですが、 RubyだとRSpecデファクト感あるのでスペックテストの方が馴染みがありそうです。

Effective Ruby

Effective Ruby

MiniTestユニットテストに慣れる

  • テストクラスを定義し、スーパークラスMiniTest::Unit::TestCase にする
  • 個々のテストケースはインスタンスメソッドとして記述し、"test_"プレフィクスをつける
  • ユニットテスト時はアサーションを使う
  • テストメソッドは短くする
  • アサーションメソッドは assert メソッドだけでなく、 assert_equal 等の適切なものを使う
  • assert_xxx に対応する refute_xxx で逆の動作を扱える
  • テストをまとめて実行するRake taskを使う(または作る)
class HogeTest < MiniTest::Unit::TestCase
  def test_hoge
    hoge = Hoge.new
    assert_equal(0, hoge.xxx)
  end
end

MiniTestスペックテストに慣れる

  • 基本的にはユニットテストをラップしてるだけ
  • describeメソッド呼び出しでクラスが自動的に定義されるため、自前でクラス定義を書く必要がない
  • アサート系メソッドの代わりにオブジェクトに注入されたエクスペクテーションメソッド( must_equal, wont_equal )を使う。
describe(Hoge) do
  describe('xxx') do
    before do
      @hoge = Hoge.new
    end
    it('returns initial value') do
      @hoge.xxx.must_equal(0)
    end
  end
end

モックオブジェクトで決定論をシミュレートする

  • 非決定的な処理(HTTPリクエスト等)からテストを切り離したいときはモックを使う
  • モックで交換するメソッドは外部ライブラリが提供している部分にすべき
  • テストメソッドを終える前に必ず verify を呼んで、モックメソッドが実行されていることを確認する。
  • MiniTest::Mockでモックを作成できるが、Mocha等の別ライブラリ使った方が高機能なのでおすすめ

インタフェースをテストする、という原則があるのですが、実装詳細に立ち入ってモックを使わざるを得ないケースもあると思います。その場合 verify でモックメソッドが確実に実行されていることをテストする必要があります。 すると、実装詳細がしれっと変わった場合でもテストで検知できるようになります。

効果的なテストを追求する

  • ハッピーパスと例外パスの両方を試すためにファズテスト、プロパティテストツールを使う
  • コードカバレッジを見て安心しない
  • 機能とテストは同時に書く(テストを後回しにしない)
  • テストは自動化する

用語等

ハッピーパステスト

テストしているコードのすべての前提条件を丁寧に準備して有効な入力しか与えないテスト。バグ発見の効果が薄い。

例外パステスト

さまざまな入力を送り込んでコードの全ての分岐先を確実に実行するテスト。一般に複雑になりすぎるが、ファズテストとプロパティテストで対処できる。

ファズテスト

プログラムや特定のメソッドにランダムデータを大量に送り込むことで、 クラッシュさせたり予想外の例外を発生させることができるかをチェックするテスト。

FuzzBert gem等で実行可能。基本的にほぼ無限にテストを行うため時間がかかる。

プロパティテスト

ファズテスト同様にランダムなデータを大量に送り込むが、コードが満たすべきプロパティ(性質)を満足するかをチェックするテスト。

MrProper gem等で実行可能。

参考: ソフトウェアの品質を学びまくる:Property-based Testing、そしてExample-based testing、とは


Rubyとは別でテスティングの理論的なところをあまり深く理解できていないので、もうちょっと勉強していきたいです。 あとは実践的なところでRSpecを使いこなせるようになりたいです。(適当にしか使えてないので。。。)

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)