大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【RSpec】within

はじめに

こんにちは!大ちゃんの駆け出し技術ブログです。

本日はかなりの短いアウトプットとなります。ど平日で仕事終わりに書いておりますが、少し体調が悪く疲れているのです!!!(言い訳ですが、、、)

本日はRUNTEQのカリキュラムの中で、これまでに学んだRSpecのマッチャやテスト方法を紹介します!というか、これから出すいくつかの記事でRSpecで使えると思ったものを紹介しようかなと思ってます。ただ、visitfill_inなどはよく使われており調べれば簡単に出てくるため、本記事で紹介はしません。これからのRSpecの記事で紹介するものは1つのテストを書いているときに使うタイミングもあれば使わないタイミングもあると思うようなものに限定します。

今回使うアプリケーションは、現場Railsのchapter7まで終えている状態のアプリケーション「taskleaf」です。同じ状態でハンズオンで試したいという方は現場Railsのchapter7までを終わらせてから取り組んでください。

使うタイミング

第1回目は記事のタイトルにあるとおりwithin!これはかなり使えるなと思いました。特に、画面にたくさんのボタンやリンクが表示されている画面でテストをする際はおそらく必須になると思います。つまり、シンプルな設計がなされているページでは使う必要がありません。

どういうタイミングで使えるのかというと、本アプリのタスク一覧画面のような設計で使えます。

https://i.gyazo.com/6c57d45629104770c080f10f7bfa412e.png

ん?すごくシンプルな画面じゃん。

そうですね。チュートリアルの画面として最適のような画面です。ではこのタスク一覧画面の中で「指定のタスクを削除する」というスペックを書きましょう。タスク一覧画面のテストなので、既に作成されているspec/system/tasks_spec.rbを使用しましょう!

# spec/system/tasks_spec.rb
describe "タスク管理機能", type: :system do
  let(:user_a) { FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com') }
  let(:user_b) { FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com') }
  let!(:task_a) { FactoryBot.create(:task, name: '最初のタスク', user: user_a) }
  before do
    # 作成者がユーザーAであるタスクを作成しておく
    FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    # 共通ログイン
    visit login_path
    fill_in 'メールアドレス', with: login_user.email
    fill_in 'session[password]',   with: login_user.password
    click_button 'ログインする'
  end

  describe 'タスク削除機能' do
    let(:login_user) { user_a }
    context '削除ボタンを押したとき' do
      binding.irb
      before { click_on '削除' }
      it 'タスクが削除される' do
        expect(current_path).to eq(root_path)
                expect(Task.all.count).to eq(1)
      end
    end
  end
・
・
・
・
end

タスク削除機能のテストではuser_aとしてログインしているため、ログイン後の画面であるタスク一覧が表示されています。

ログイン時画面

f:id:SakitaDaiki:20210218185506p:plain

削除ボタンが見えますね。ですのでタスクを1つ削除するとタスクの数は一つとなるのでexpect(Task.all.count).to eq(1)がパスするという想定です。ですが、、、

ターミナル

$ bundle exec rspec spec/system/tasks_spec.rb
2021-02-18 18:12:42 WARN Selenium [DEPRECATION] Selenium::WebDriver::Chrome#driver_path= is deprecated. Use Selenium::WebDriver::Chrome::Service#driver_path= instead.
Capybara starting Puma...
* Version 3.12.6 , codename: Llamas in Pajamas
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:61796
F

Failures:

  1) タスク管理機能 タスク削除機能 削除ボタンを押したとき タスクが削除される
     Failure/Error: before { click_on '削除' }
     
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching visible link or button "削除"
     
     [Screenshot]: tmp/screenshots/failures_r_spec_example_groups_nested_nested_nested_タスクが削除される_422.png

     
     # ./spec/system/tasks_spec.rb:20:in `block (4 levels) in <top (required)>'

Finished in 5.21 seconds (files took 2.19 seconds to load)
1 example, 1 failure

テストが失敗していますね。何故でしょうか。

その理由は同じ文字のボタンが複数個表示されているからです。

ログインすると2つのタスクが作成されていることは上述しました。しかし、そのタスク削除ボタンの文字はお互いに「削除」です。エラーメッセージを見てみると、

Ambiguous match, found 2 elements matching visible link or button "削除"

Ambiguousとあまり見ない英単語ですね。意味は「曖昧な」。つまり、Ambiguous matchとは「曖昧な一致状態」というエラーになります。そこから表示されているエラーメッセージは、「表示されているリンクまたはボタンに文字列”削除”と一致する2つの要素が見つかりました」。

この文から分かるとおり、「"削除"という文字のボタンが2つあるので、どっちのボタンを指定しているのかが曖昧です」とエラーメッセージを返しているわけです。

Withinの使用方法

では上記エラーを解消しましょう。解消する方法はわりとシンプルです。

before do
  within '#task-1' do
    click_on '削除'
  end
end

各タスクのtrには固有のidが設定されています。

https://i.gyazo.com/f66bb0f60b18088487a2f0f295f8aee7.png

つまり、そのタスクのidを指定すればその中の削除ボタンを「idが〜の削除ボタン」と固有のものにできます。within '#task-1'で指定している#task-1の箇所はCSSを指定する時と同じように、idまたはクラスを指定することができます。そして、指定した要素の内部にある要素のみにRSpecのマッチャの対象を指定することができます。

これにより2つの削除ボタンを差別化することができ、テストをパスすることができるというわけです!

終わりに

かなり短めで申し訳ないです!もっと時間を取れるときにたくさん抹茶を紹介できればと思いますので多めに見てくれるとありがたいです!

以上、大ちゃんの駆け出し技術ブログでした!