テストの基本構造

2013/08/16

今回は、前回作った「Hello World!」のテスト(テストコード)の説明をしながら、RSpec/Capybara によるテストの基本構造について解説します。

エグザンプルグループとエグザンプル

spec/features/top_page_spec.rb の中身を見てください:

require 'spec_helper'

describe 'トップページ' do
  specify '挨拶文を表示' do
    visit root_path
    expect(page).to have_css('p', text: 'Hello World!')
  end
end

1行目の require 'spec_helper' で、RSpec の設定ファイル spec/spec_helper.rb を読み込んでいます。Rails アプリケーションのテストをする場合には、この記述が必須です。

その下に do ... end ブロックが do ... end ブロックを包み込むような構造があります。前者の describe ブロックをエグザンプルグループ、後者の specify ブロックをエグザンプルと呼びます。

「エグザンプル(example)」には「実例」とか「模範」などの意味がありますが、RSpec 用語としては「定義したい仕様のある側面」といった意味合いです。top_page_spec.rb の目的は「トップページ」の仕様を定義することです。現時点でのトップページには h1 要素と p 要素が1つずつ存在するだけですが、開発が進むにつれて様々な機能やデザイン要素が追加されていくはずです。それら一つひとつを「エグザンプル」と呼びます。

「エグザンプルグループ(example group)」は文字通り1個ないし複数個のエグザンプルをまとめるための構造ですが、describe(「記述する」)というメソッド名が使われていることからも分かるように、単なるグループ分けというよりも仕様を定義したい対象物を指定する目的で使用します。

Capybara でトップページを訪問する

Capybara

テストの5行目を見てください。visit root_path とあります。

visit は Capybara が提供するメソッドです。引数に指定したURLパスを訪問(visit)します。すなわち、仮想的な Web ブラウザで Rails アプリケーションにアクセスします。より正確に言えば、HTTP をエミュレートして Rails アプリケーションと交信し、レスポンスを解析します。

他方、root_path は Rails に由来するメソッドで、/ という文字列を返します。visit '/' と記述しても動きますが、意図を明確にするため visit root_path と書いています。

Rails アプリケーションからのレスポンスが HTML 文書であった場合、その内容は page メソッドで取得できます。次の6行目に出てきます。

expect メソッド

RSpec のコードを初めて見る方にとって、6行目の expect(page).to have_css('p', text: 'Hello World!') は奇妙に映るかもしれません。ここは tohave_css の間で分けて考えると分かりやすいです。

前半の

expect(page).to

は、「pageto 以下の性質を期待する(expect)」という意味です。前述のように、page は Capybara が Rails アプリケーションから受け取った HTML 文書の中身を返します。メソッド to については後述します。

後半の

have_css('p', text: 'Hello World!')

マッチャ(matcher)と呼ばれるものの一種です。マッチャは expect で指定されたオブジェクトが特定の条件に合致する(match)かどうかを調べ、合致しなければ RSpec はテストが失敗したと判定します。

have_css は Capybara が提供するマッチャです。引数に指定した CSS セレクタに適合する要素が HTML 文書中に存在するかどうかを調べます。また、その他の条件をオプションで指定することもできます。ここでは、中身のテキストが「Hello World!」であるような p 要素が存在することを確認しています。

should はどこに行ったの?

ところで、2012年より前に RSpec に触れて最近の変化を追っていない方は、「should はどこに行ったのか」と不審に思われるかもしれません。先ほどのテストの6行目、かつては次のように書かれていましたね。

page.should have_css('p', text: 'Hello World!')

しかし、2011年7月リリースの RSpec 2.11 で新たな書き方が導入され、x.shouldexpect(x).to と書くことになりました。ただし、古い書き方もまだ使えますし、次の RSpec 3 でも廃止される予定はありません。しかし、RSpec のコアチームはできれば RSpec 4 では廃止したいと考えているようです(世の中の理解が進めば、ということですね)。

彼らが x.should の代わりに expect(x).to を推奨したいと考えている理由については、コアチームのメンバーである Myron Marston 氏が自身のブログ記事 RSpec's New Expectation Syntax で詳しく説明しています。また、第1回で紹介したRSpec 3に向けての計画(日本語訳)にも説明があります。

元来、x.should は、テストコードが英語の文のように見えるために考案されたものでした。新しい構文は少しキーストローク数が増えますが、メソッド to が英語の不定詞を導く前置詞 to の位置にあるので、「英語の文のように見える」点は変わっていません。中学・高校の英語の授業で習う「基本5文型」で言えば、「第2文型 SVC」から「第5文型 SVOC」に変わっただけです。

なお、should メソッドの利用を禁止するには、spec/spec_helper.rb を次のように修正します。

# (省略)

RSpec.configure do |config|
  # (省略)

  config.expect_with :rspec do |c|
    c.syntax = :expect
  end
end

should は RSpec 草創期からある最も目立つ特徴なので惜しむ声も聞かれますが、私自身は新しい書き方が気に入っています。

次回は

次回はモデルのテストについて書くつもりです。では、また。