assert_selectを極めたい（基本編）【Rails】【minitest】

RailsのMinitestを用いてViewのテストをする場合に使用する`assert_select`を極めるために試したことの備忘録です。
その中でも基本的な使い方の部分について今回まとめてみました。
Minitestを使ってViewテストをしたい人の参考になればと思います。

## 書式
``` ruby
assert_select "条件となる要素(セレクタ)", "[{条件}]", "[メッセージ]"
assert_select "対象となる要素", "条件となる要素", "[{条件}]", "[メッセージ]"
```
`assert_select`は上記の書式で利用可能です。`[]`の内容に関しては省略可能です。
`メッセージ`はエラー時に表示する文章を指定できます。エラーの内容を指定した文章で明示的に示すことができますが、今回は使用しておりません。

## 使い方
### 要素の存在チェック
`assert_select`では単に指定した要素が存在するかどうかだけでなく、要素の個数やテキストを指定して存在チェックすることが可能です。
``` ruby
# 存在チェック
assert_select "section" # 1つ以上存在
assert_select "form", false # 要素が存在しない(0でも同じ)

# 要素の数を指定する
assert_select "section", count: 6
assert_select "section", 6 # count省略可能
assert_select "section", 1..6 # 範囲指定

# 要素のテキストをチェック
assert_select "h1", text: "見出し"
assert_select "h1", "見出し" # text省略可能
assert_select "h1", /見出/ # 正規表現

# 条件を複数指定する場合
assert_select "h1", { count: 1, text: '見出し' } # 条件はand条件
assert_select "h1", count: 1, text: '見出し' # {}は省略可能

# NGな書き方(複数条件指定にはならない)
assert_select "h1", 1, '条件外' # この場合、テキストの条件ではなくメッセージと見なされてしまうのでカウントしか条件にならない。
```

### タイプセレクタ以外のセレクタを利用したチェック
`assert_select`で指定する条件となる要素は、cssセレクタを利用して設定します。
``` ruby
# id, classセレクタ
assert_select "h1#title"
assert_select ".section"
# 子孫要素をチェック
assert_select "main section" # 子孫要素全て
assert_select "main > section" # 子要素のみ
# 擬似クラスを利用したチェック
assert_select "section p:first-child" # 最初の要素
assert_select "section p:only-of-type" # 兄弟要素に同じ要素がない
assert_select "section div:empty" # 中身空
# 属性セレクタ
assert_select "input[name=sample]"
assert_select "a[href='https://sample.jp']"
# 複数セレクタの指定
assert_select "input, select" # どちらかの条件にマッチする要素
```
上に挙げたもの以外にも、多くのcssセレクタが存在します。条件を組み合わせて、テストしたい内容を実現してください。

### 属性セレクタの値をチェック
属性セレクタの値をチェックする方法はいくつか存在します。
value値や選択されている値が合っているか等をチェックする時に利用できます。
``` ruby
# 直接指定する場合
assert_select "input[name=sample]"
# 置換式を利用
assert_select "input:match('name', ?)", 'sample', { count: 1 }
assert_select "input:match('name', ?):match('value', ?)", 'sample', '1', { count: 1 }
assert_select "input[name=?]", 'sample', { count: 1 }
assert_select "input[name=?][value=?]", 'sample', '1', { count: 1 }

# 条件式を利用
assert_select "input:match('name', ?)", /^samp.*/ # 正規表現
assert_select "input[name^=?]", 'samp' # 前方一致
assert_select "input[name$=?]", 'ple' # 後方一致
assert_select "input[name*=?]", 'ampl' # 部分一致
```

### タグの階層に合わせてチェック
ネストしたassert_selectブロックを用いることができます。
外側で指定した条件に一致した全ての要素に対して、内側で指定した条件の判定が行われます。
``` ruby
# ulが2つあってそれぞれliを3つずつ持っている場合
# 直接指定
assert_select "ul li", 6

# 階層に合わせてネスト
assert_select "ul", 2 do
  assert_select "li", 6
end
assert_select "ul", 2 do |elements|
  elements.each do |element|
    assert_select element, "li", 3
  end
end

# ネストして要素の条件を繋げる
assert_select "input" do # 1要素に対して複数属性チェックする場合は見やすくなる
  assert_select ":match('name', ?)", 'sample'
  assert_select ":match('value', ?)", '1'
  assert_select "[name=?]", 'sample'
end
assert_select 'main' do
  assert_select '>section', 2 # >などのセレクタも繋げること可能
end
```

## 終わりに
今回まとめた使い方で多くのViewのテストが実現できるようになると思います。
次回は実践編という形でシチュエーションに合わせたテストの書き方をまとめられたらなと考えています。

将来の快適なコーディングライフのためにも、しっかりとテストは書いていきましょう！

## 参考
[Rails テスティングガイド](https://railsguides.jp/testing.html)
[rails-dom-testing/selector_assertions.rb at master · rails/rails-dom-testing](https://github.com/rails/rails-dom-testing/blob/master/lib/rails/dom/testing/assertions/selector_assertions.rb)