マクロ（ヘルパーメソッド）を定義してフィーチャースペックのユーザー切替えを楽に行う


## はじめに

フィーチャースペックを書いていると、ユーザー1人による操作ではなく、2人以上のユーザーを切り替えながら操作しないと、テストとして不十分な場合があります。
ただ、ユーザーを切り替えるたびにログインとログアウトの処理を毎回書くのは面倒です。
そんな場合は毎回ログイン・ログアウトの処理を書かなくて済むようなマクロ（ヘルパーメソッド）を定義しておくと便利です。

## マクロを使わない例

たとえば掲示板サービスを開発していたとします。
あなたはこのサービスで以下のようなテストを書きたいと考えました。

1. ユーザーAでログインする
2. ユーザーAが親トピックを投稿する
3. ユーザーBでログインする
4. ユーザーBが2で作成したトピックに返信する

マクロを使わない場合、次のようなフィーチャースペックになります。

```ruby
# spec/factories/users.rb
FactoryGirl.define do
  factory(:user) do
    sequence(:email) { |n| "user-#{n}@example.com" }
    password '12345678'
  end
end
```

```ruby
# spec/features/message_board_spec.rb
require 'rails_helper'

feature '掲示板' do
  given(:alice) { create :user }
  given(:bob) { create :user }
  
  scenario 'トピックの投稿とコメントの返信' do
    # aliceとして操作
    visit sign_in_path
    fill_in 'email', with: alice.email
    fill_in 'password', with: '12345678'
    click_button 'ログイン'
    expect(page).to have_content 'ログインしました。'

    visit root_path
    fill_in '投稿内容', with: '59年製ギブソンレスポールを3万円で譲ります。'
    click_button '投稿する'
    expect(page).to have_content '投稿しました。'

    click_link 'ログアウト'
    expect(page).to have_content 'ログアウトしました。'

    # bobとして操作
    visit sign_in_path
    fill_in 'email', with: bob.email
    fill_in 'password', with: '12345678'
    click_button 'ログイン'
    expect(page).to have_content 'ログインしました。'

    visit root_path
    fill_in 'コメント', with: 'はいはいはい！！！ほしいです！！！！'
    click_button '返信する'
    expect(page).to have_content '返信しました。'
  end
end
```

## マクロを使う例・基本編

しかし、上のコードはログインとログアウトの処理が冗長です。
そんな場合はマクロを定義するとテストコードがシンプルになります。

まず、以下のようにしてログインとログアウトを実行するマクロを使えるようにします。

```ruby
# spec/support/login_macros.rb
module LoginMacros
  def login(user)
    visit sign_in_path
    fill_in 'email', with: user.email
    fill_in 'password', with: '12345678'
    click_button 'ログイン'
    expect(page).to have_content 'ログインしました。'
  end

  def logout
    click_link 'ログアウト'
    expect(page).to have_content 'ログアウトしました。'
  end
end
```

```ruby
# spec/rails_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  config.include LoginMacros
end
```

このマクロを使うと先ほどのフィーチャースペックはこうなります。

```ruby
# spec/features/message_board_spec.rb
feature '掲示板' do
  given(:alice) { create :user }
  given(:bob) { create :user }
  
  scenario 'トピックの投稿とコメントの返信' do
    # aliceとして操作
    login(alice)

    visit root_path
    fill_in '投稿内容', with: '59年製ギブソンレスポールを3万円で譲ります。'
    click_button '投稿する'
    expect(page).to have_content '投稿しました。'

    logout

    # bobとして操作
    login(bob)

    visit root_path
    fill_in 'コメント', with: 'はいはいはい！！！ほしいです！！！！'
    click_button '返信する'
    expect(page).to have_content '返信しました。'
  end
end
```

先ほどよりもテストコードがシンプルになりましたね。

## マクロを使う例・発展編

しかし、`login`、`logout`すら毎回書くのは面倒です。
そこでもっと簡単にユーザーを切り替えるために、次のようなマクロを追加します。

```ruby
# spec/support/login_macros.rb
module LoginMacros
  # 省略
  
  # ログイン・ログアウトの手順を省略するマクロ
  def act_as(user)
    login user
    yield
    logout
  end
end
```

この`act_as`メソッドは「ログイン => ブロックで書いた処理を実行（`yield`） => ログアウト」を実行してくれます。
以下は`act_as`メソッドの使用例です。

```ruby
act_as alice do
  visit root_path
  # その他の処理
end
```

`act_as`メソッドを先ほどのテストコードに組み込んでみましょう。

```ruby
# spec/features/message_board_spec.rb
feature '掲示板' do
  given(:alice) { create :user }
  given(:bob) { create :user }
  
  scenario 'トピックの投稿とコメントの返信' do
    # aliceとして操作
    act_as alice do
      visit root_path
      fill_in '投稿内容', with: '59年製ギブソンレスポールを3万円で譲ります。'
      click_button '投稿する'
      expect(page).to have_content '投稿しました。'
    end

    # bobとして操作
    act_as bob do
      visit root_path
      fill_in 'コメント', with: 'はいはいはい！！！ほしいです！！！！'
      click_button '返信する'
      expect(page).to have_content '返信しました。'
    end
  end
end
```

ご覧のとおり、テストコードから冗長なログイン・ログアウトの処理を無くすことができました。
そればかりでなく、aliceの操作とbobの操作がブロックとしてインデントされるので、どの行がどちらのユーザーで操作しているのかもわかりやすくなっています。

## まとめ

このように、毎回登場する面倒なコードはマクロとして定義しておくと便利です。
`act_as`メソッドのようにブロックも活用すれば、さらに見通しのよいテストコードが書ける場合があります。
よかったら参考にしてみてください。

### PR: 「Everyday Rails - RSpecによるRailsテスト入門」でRSpecを学習しましょう！

「RSpecでテストコードを書くのが苦手」という人は電子書籍「[Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)」がオススメです。
今回説明したようなマクロの定義も載っています。
まだ読んでいない方はぜひチェックしてみてください！

[Everyday Rails \- RSpecによるRailsテスト入門![20161207084257.png](https://qiita-image-store.s3.amazonaws.com/0/7465/3e2a5c01-0bb2-41a8-9873-756d993857d5.png "20161207084257.png")](https://leanpub.com/everydayrailsrspec-jp)

ちなみに、2017年中にはRails 5 + RSpec 3.5版にアップデートする予定です。
詳しくはこちらのブログ記事をご覧ください。

[【翻訳】Rails 5およびRSpec 3\.5対応版「Everyday Rails \- RSpecによるRailsテスト入門」のアップデートについて \- give IT a try](http://blog.jnito.com/entry/2016/12/07/084816)