コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.11 - Test coding -

# はじめに
第11回目ですね。
前回はテストを自動化するために<b>RSpec</b>、<b>Selenium</b>、<b>Capybara</b>などを導入しましたね。

今日は今まで作ってきたアプリケーションに対してテストコードをコーディングしていきます。
本当はアプリをコーディングする前にテストをコーディングして<b>Red</b>のフェーズにするべきなのですが、
まぁ最初なのでご愛嬌ということで<b>Green</b>の状態から始めましょう。

# 前回のソースコード
前回のソースコードはこちらに格納してます。今回のハンズオンからやりたい場合はこちらからダウンロードしてください。

- https://github.com/at946/rails_on_docker_handson/tree/vol.10

# どういうふうにテストコード書いてくの？
ここは人によってやりやすいようにでいいと思うのですが、このハンズオンでは基本的には作りたい機能（ユーザーストーリー）ごとにテストファイルを分けて記述していきます。
例えば、今までだと「サインアップ」とか「サインイン」とかそういうやつです。

例えば以下のようにテストシナリオを考えてみます。

### 1. ユーザーとして、ページにダイレクトアクセスしたい
1. 未サインインのユーザーが、トップページにダイレクトアクセスしたとき、トップページが表示されること
- 未サインインのユーザーが、サインアップページにダイレクトアクセスしたとき、サインアップページが表示されること
- 未サインインのユーザーが、サインインページにダイレクトアクセスしたとき、サインインページが表示されること
- 未サインインのユーザーが、ユーザー詳細ページにダイレクトアクセスしたとき、ユーザー詳細ページが表示されること
- サインイン済のユーザーが、トップページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること
- サインイン済のユーザーが、サインアップページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること
- サインイン済のユーザーが、サインインページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること
- サインイン済のユーザーが、ユーザー詳細ページにダイレクトアクセスしたとき、ユーザー詳細ページが表示されること

### 2. ユーザーとして、ヘッダーリンクからページ遷移できること
1. 未サインインのユーザーが、トップページでヘッダーのロゴをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、トップページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、トップページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること
- 未サインインのユーザーは、トップページでヘッダーに「Profile」リンクが存在しないこと
- 未サインインのユーザーは、トップページでヘッダーに「Sign out」リンクが存在しないこと
- 未サインインのユーザーが、サインアップページでヘッダーのロゴをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、サインアップページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、サインアップページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること
- 未サインインのユーザーは、サインアップページでヘッダーに「Profile」リンクが存在しないこと
- 未サインインのユーザーは、サインアップページでヘッダーに「Sign out」リンクが存在しないこと
- 未サインインのユーザーが、サインインページでヘッダーのロゴをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、サインインページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、サインインページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること
- 未サインインのユーザーは、サインインページでヘッダーに「Profile」リンクが存在しないこと
- 未サインインのユーザーは、サインインページでヘッダーに「Sign out」リンクが存在しないこと
- 未サインインのユーザーが、ユーザー詳細ページでヘッダーのロゴをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、ユーザー詳細ページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること
- 未サインインのユーザーが、ユーザー詳細ページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること
- 未サインインのユーザーは、ユーザー詳細ページでヘッダーに「Profile」リンクが存在しないこと
- 未サインインのユーザーは、ユーザー詳細ページでヘッダーに「Sign out」リンクが存在しないこと
- サインイン済のユーザーが、ユーザー詳細ページでヘッダーのロゴをクリックしたとき、そのユーザーのユーザー詳細ページに遷移すること
- サインイン済のユーザーは、ユーザー詳細ページでヘッダーに「Home」リンクが存在しないこと
- サインイン済のユーザーは、ユーザー詳細ページでヘッダーに「Sign in」リンクが存在しないこと
- サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Profile」リンクをクリックしたとき、そのユーザーのユーザー詳細ページに遷移すること
- サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Sign out」リンクが存在すること

### 3. ユーザーとして、サインアップしたい
1. 未サインインのユーザーが、トップページで「Sign up now!」ボタンを選択したとき、サインアップページに遷移すること
- サインアップページで「お名前」を入力できること
- サインアップページで「メールアドレス」を入力できること
- サインアップページで「パスワード」を入力できること
- サインアップページで「パスワード」はマスク化されること
- サインアップページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスにチェックをいれたとき、「パスワード」が表示されること
- サインアップページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスのチェックを外したとき、「パスワード」がマスク化されること
- サインアップページで「お名前」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「お名前」未入力のエラーメッセージが表示されること
- サインアップページで「お名前」を51文字以上入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「お名前」文字数超過のエラーメッセージが表示されること
- サインアップページで「メールアドレス」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」未入力のエラーメッセージが表示されること
- サインアップページで「メールアドレス」を256文字以上入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」文字数超過のエラーメッセージが表示されること
- サインアップページで「メールアドレス」を誤ったフォーマットで入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」フォーマットチェックエラーのエラーメッセージが表示されること
- サインアップページで「メールアドレス」がすでに登録済みのメールアドレスを入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」重複のエラーメッセージが表示されること
- サインアップページで「パスワード」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「パスワード」文字数不足のエラーメッセージが表示されること
- サインアップページで「パスワード」を5文字以下で入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「パスワード」文字数不足のエラーメッセージが表示されること
- サインアップページで「お名前」「メールアドレス」「パスワード」を正しく入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは成功し、そのユーザーのユーザー詳細ページにサインイン済状態で遷移すること
- サインアップに成功したユーザーは、遷移後のユーザー詳細ページで自分の入力した「お名前」を確認できること
- サインアップに成功したユーザーは、遷移後のユーザー詳細ページで自分の入力した「メールアドレス」を確認できること
- サインアップに成功したユーザーは、遷移後のユーザー詳細ページでウェルカムメッセージを確認できること
- サインアップに成功したユーザーは、遷移後のユーザー詳細ページをリロードしたとき、ウェルカムメッセージを確認できなくなること
- サインアップページで「登録済みの方はこちら」リンクを選択したとき、サインインページに遷移すること

### 4. ユーザーとして、サインインしたい
1. サインインページで「メールアドレス」を入力できること
- サインインページで「パスワード」を入力できること
- サインインページで「パスワード」はマスク化されること
- サインインページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスにチェックをいれたとき、「パスワード」が表示されること
- サインインページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスのチェックを外したとき、「パスワード」がマスク化されること
- サインインページで「メールアドレス」を入力していないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること
- サインインページで「メールアドレス」として登録されていないメールアドレスを入力したユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること
- サインインページで「メールアドレス」は正しいが「パスワード」を入力していないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること
- サインインページで「メールアドレス」は正しいが「パスワード」が正しくないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること
- サインインページで「メールアドレス」「パスワード」に正しい値を入力したユーザーが、「Sign in」ボタンをクリックしたとき、サインイン済状態でそのユーザーのユーザー詳細ページに遷移すること
- サインインに成功したユーザーは、遷移後のユーザー詳細ページでサインイン成功メッセージを確認できること
- サインインに成功したユーザーは、遷移後のユーザー詳細ページをリロードしたとき、サインイン成功メッセージを確認できなくなること
- サインインページで「登録がまだの方はこちら」リンクを選択したとき、サインアップページに遷移すること

### 5. ユーザーとして、サインアウトしたい
1. サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Sign out」リンクをクリックしたとき、未サインイン状態になりトップページに遷移すること

### 6. ユーザーとして、他のユーザーの情報を閲覧したい
1. ユーザーが、存在するユーザーのユーザー詳細ページにアクセスしようとしたとき、そのユーザーの「お名前」「メールアドレス」を確認できること
2. ユーザーが、存在しないユーザーのユーザー詳細ページにアクセスしようとしたとき、エラーが発生すること

ざっとあげただけでもこれだけのテストシナリオがあります。
こんなにコード書かなきゃいけないのかよ！と思うかもしれませんが、コードを書かないと少しのリファクタリングの度にこれら全てのテストを手動で行わなければ安心してデプロイできないという修羅の道を選ぶことになります。
今日でテストコードへのハードルを爆下げして気軽にリファクタできるエンジニアをめざしましょう！

# テストコードを書いていこう
ここから実際にテストコードを書いていきます。
上でナンバリングで章立てしてましたね。それごとにスペックファイルを作って管理します。

## 1. ユーザーとして、ページにダイレクトアクセスしたい
ファイル名は`01_direct_access_spec.rb`にしておきましょう。

```
$ mkdir spec/system/
$ touch spec/system/01_direct_access_spec.rb
```

```rb:01_direct_access_spec.rb
feature "ユーザーとして、ページにダイレクトアクセスしたい", type: :system do

  background do
    @user1 = User.create(name: "John Smith", email: "john@sample.com", password: "john1234")
    @user2 = User.create(name: "Taro Tanaka", email: "taro@sample.com", password: "taro1234")
  end
  
  scenario "未サインインのユーザーが、トップページにダイレクトアクセスしたとき、トップページが表示されること" do
    visit root_path
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、サインアップページにダイレクトアクセスしたとき、サインアップページが表示されること" do
    visit sign_up_path
    expect(current_path).to eq sign_up_path
  end

  scenario "未サインインのユーザーが、サインインページにダイレクトアクセスしたとき、サインインページが表示されること" do
    visit sign_in_path
    expect(current_path).to eq sign_in_path
  end

  scenario "未サインインのユーザーが、ユーザー詳細ページにダイレクトアクセスしたとき、ユーザー詳細ページが表示されること" do
    visit user_path(@user1)
    expect(current_path).to eq user_path(@user1)

    visit user_path(@user2)
    expect(current_path).to eq user_path(@user2)
  end

  feature nil, type: :system do

    background do
      visit sign_in_path
      fill_in :user_email, with: @user1.email
      fill_in :user_password, with: @user1.password
      click_on :sign_in_button      
    end

    scenario "サインイン済のユーザーが、トップページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること" do
      visit root_path
      expect(current_path).to eq user_path(@user1)
    end
  
    scenario "サインイン済のユーザーが、サインアップページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること" do
      visit sign_up_path
      expect(current_path).to eq user_path(@user1)
    end
  
    scenario "サインイン済のユーザーが、サインインページにダイレクトアクセスしたとき、そのユーザーのユーザー詳細ページが表示されること" do
      visit sign_in_path
      expect(current_path).to eq user_path(@user1)
    end
  
    scenario "サインイン済のユーザーが、ユーザー詳細ページにダイレクトアクセスしたとき、ユーザー詳細ページが表示されること" do
      visit user_path(@user1)
      expect(current_path).to eq user_path(@user1)
  
      visit user_path(@user2)
      expect(current_path).to eq user_path(@user2)
    end
      
  end

end
```
このテストをパスさせるために、サインインページに少し細工をします。

```diff:app/views/sessions/new.html.erb
- <%= form.submit "Sign in", class: "btn btn-primary form-control" %>
+ <%= form.submit "Sign in", class: "btn btn-primary form-control", id: :sign_in_button %>
```
`id: :sign_in_button`を追記しました。これで`id`属性を追加できます。

まずはテストがパスするのを体感しましょうか！

```
$ docker-compose up -d
$ docker-compose exec web ash
```

```
# rspec spec/system/01_direct_access_spec.rb
Capybara starting Puma...
* Version 4.3.1 , codename: Mysterious Traveller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:45497
........

Finished in 11.85 seconds (files took 5.4 seconds to load)
8 examples, 0 failures
```
テストパスしましたね！
どんなテストが実行されたのか、ちょっと紹介させてください！

### 構文
前回も紹介しましたが、RSpecのシステムテストの構文は

```rb
feature "test name", type: :system do
  scenario "test scenario" do
    # テストコード
  end
end
```
です。`scenario`は複数あります。

`feature`内の全ての`scenario`に適用する初期条件を記述する場合は`background`を使います。

```rb
feature "test name", type: :system do
  background do
    # 前提条件
  end

  scenario "test scenario" do
    # テストコード
  end
end
```
また、`feature`を入れ子にすることも可能です。これによって特定の`scenario`たちにだけ前提条件をつけることも可能です。

```rb
feature "test name", type: :system do
  scenario "test scenario" do
    # 前提条件が適用されない
  end

  feature "test detail name", type: :system do
    background do
      # 前提条件
    end

    scenario "test scenario" do
      # 前提条件が適用される    
    end
  end
end
```
まずはこの構文を身に付けましょう。

### background
`background`は前提条件を定義するためのブロックです。そのファイルのシナリオに共通して行われる処理をここで定義します。
よく使われる場面としては、今回のようにモデルを作っておく、とかですね。
モデルは今までのRubyコードと同じように、`User.create`や`User.new`が使えます。また、インスタンス変数にしないと`scenario`側では参照できないので注意してくださいね。

### visit
`visit`は引数に名前付きルート（xxxx_path）やURLをとって、そこにアクセスします。

```rb
visit root_path
```
これで`root_path`、つまり`/`にアクセスをしようとします。

### expect().to
`expect().to`は`()`内を`to`以降と検証します。
例えば`expect().to eq xxxxx`のように`eq`と組み合わせることで`()`内と`xxxxx`が等しいことを検証します。
この検証が`false`の場合はそのシナリオをFailureになります。

### current_path
`current_path`は現在表示されているパス（`/`とか`/users/1`とか）を取得します。

```rb
expect(current_path).to eq root_path
```
で、今表示されているページのパスがルートパス、つまりトップページであるかどうかを検証しているのです。

### fill_in
`fill_in`は`input`に文字を入力する操作を実行します。

```rb
fill_in [id], with: [入力したい文字列]
```
で、ページから`id`属性が`[id]`の`input`に`[入力したい文字列]`を入力してくれます。

### click_on
`click_on`は`<a>`または`<button>`タグをクリックする操作を実行します。

```rb
click_on [id]
```
で、ページから`id`属性が`[id]`の`<a>`か`<button>`タグをクリックしてくれます。

ここまでを理解すると

```rb
background do
  visit sign_in_path
  fill_in :user_email, with: @user1.email
  fill_in :user_password, with: @user1.password
  click_on :sign_in_button
end
```
が、サインインページにアクセスして`@user1`でサインインしようとしていることがわかりますね？

こんな感じで操作と検証を組み合わせてテストを自動化していきます！
ではどんどんテストコードを書いていきましょう！！

## 2. ユーザーとして、ヘッダーリンクからページ遷移できること
ファイル名は`02_header_spec.rb`でいきましょうか。

```
# touch spec/system/02_header_spec.rb
```

また少し`id`を仕込んでおきましょう。

```diff:app/views/layouts/application.html.erb
  <div class="container">
-   <%= link_to "sample app", root_path, class: "navbar-brand" %>
+   <%= link_to "sample app", root_path, class: "navbar-brand", id: :header_logo %>
    <ul class="navbar-nav">
      <% if signed_in? %>
        <%# サインイン済みの場合のリンク %>
-       <li class="nav-item"><%= link_to "Profile", current_user, class: "nav-link" %></li>
-       <li class="nav-item"><%= link_to "Sign out", sign_out_path, method: :delete, class: "nav-link" %></li>
+       <li class="nav-item"><%= link_to "Profile", current_user, class: "nav-link", id: :header_profile_link %></li>
+       <li class="nav-item"><%= link_to "Sign out", sign_out_path, method: :delete, class: "nav-link", id: :header_sign_out_link %></li>
      <% else %>
        <%# 未サインインの場合のリンク %>
-       <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link" %></li>
-       <li class="nav-item"><%= link_to "Sign in", sign_in_path, class: "nav-link" %></li>
+       <li class="nav-item"><%= link_to "Home", root_path, class: "nav-link", id: :header_home_link %></li>
+       <li class="nav-item"><%= link_to "Sign in", sign_in_path, class: "nav-link", id: :header_sign_in_link %></li>
      <% end %>
    </ul>
  </div>
``` 
そしてテストシナリオを書きます。

```rb:spec/system/02_header_spec.rb
feature "ユーザーとして、ヘッダーリンクからページ遷移できること", type: :system do
  background do
    @user1 = User.create(name: "John Smith", email: "john@sample.com", password: "john1234")
    @user2 = User.create(name: "Taro Tanaka", email: "taro@sample.com", password: "taro1234")
  end

  scenario "未サインインのユーザーが、トップページでヘッダーのロゴをクリックしたとき、トップページに遷移すること" do
    visit root_path
    click_on :header_logo
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、トップページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること" do
    visit root_path
    click_on :header_home_link
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、トップページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること" do
    visit root_path
    click_on :header_sign_in_link
    expect(current_path).to eq sign_in_path
  end

  scenario "未サインインのユーザーは、トップページでヘッダーに「Profile」リンクが存在しないこと" do
    visit root_path
    expect(page).not_to have_selector "#header_profile_link"
  end

  scenario "未サインインのユーザーは、トップページでヘッダーに「Sign out」リンクが存在しないこと" do
    visit root_path
    expect(page).not_to have_selector "#header_sign_out_link"
  end

  scenario "未サインインのユーザーが、サインアップページでヘッダーのロゴをクリックしたとき、トップページに遷移すること" do
    visit sign_up_path
    click_on :header_logo
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、サインアップページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること" do
    visit sign_up_path
    click_on :header_home_link
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、サインアップページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること" do
    visit sign_up_path
    click_on :header_sign_in_link
    expect(current_path).to eq sign_in_path
  end

  scenario "未サインインのユーザーは、サインアップページでヘッダーに「Profile」リンクが存在しないこと" do
    visit sign_up_path
    expect(page).not_to have_selector "#header_profile_link"
  end

  scenario "未サインインのユーザーは、サインアップページでヘッダーに「Sign out」リンクが存在しないこと" do
    visit sign_up_path
    expect(page).not_to have_selector "#header_sign_out_path"
  end

  scenario "未サインインのユーザーが、サインインページでヘッダーのロゴをクリックしたとき、トップページに遷移すること" do
    visit sign_in_path
    click_on :header_logo
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、サインインページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること" do
    visit sign_in_path
    click_on :header_home_link
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、サインインページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること" do
    visit sign_in_path
    click_on :header_sign_in_link
    expect(current_path).to eq sign_in_path
  end

  scenario "未サインインのユーザーは、サインインページでヘッダーに「Profile」リンクが存在しないこと" do
    visit sign_in_path
    expect(page).not_to have_selector "#header_profile_path"
  end

  scenario "未サインインのユーザーは、サインインページでヘッダーに「Sign out」リンクが存在しないこと" do
    visit sign_in_path
    expect(page).not_to have_selector "#header_sign_out_path"
  end

  scenario "未サインインのユーザーが、ユーザー詳細ページでヘッダーのロゴをクリックしたとき、トップページに遷移すること" do
    visit user_path(@user1)
    click_on :header_logo
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、ユーザー詳細ページでヘッダーの「Home」リンクをクリックしたとき、トップページに遷移すること" do
    visit user_path(@user1)
    click_on :header_home_link
    expect(current_path).to eq root_path
  end

  scenario "未サインインのユーザーが、ユーザー詳細ページでヘッダーの「Sign in」リンクをクリックしたとき、サインインページに遷移すること" do
    visit user_path(@user1)
    click_on :header_sign_in_link
    expect(current_path).to eq sign_in_path
  end

  scenario "未サインインのユーザーは、ユーザー詳細ページでヘッダーに「Profile」リンクが存在しないこと" do
    visit user_path(@user1)
    expect(page).not_to have_selector "#header_profile_link"
  end

  scenario "未サインインのユーザーは、ユーザー詳細ページでヘッダーに「Sign out」リンクが存在しないこと" do
    visit user_path(@user1)
    expect(page).not_to have_selector "#header_sign_out_link"
  end

  feature nil, type: :system do

    background do
      visit sign_in_path
      fill_in :user_email, with: @user1.email
      fill_in :user_password, with: @user1.password
      click_on :sign_in_button            
    end

    scenario "サインイン済のユーザーが、ユーザー詳細ページでヘッダーのロゴをクリックしたとき、そのユーザーのユーザー詳細ページに遷移すること" do
      visit user_path(@user2)
      click_on :header_logo
      sleep 1
      expect(current_path).to eq user_path(@user1)
    end

    scenario "サインイン済のユーザーは、ユーザー詳細ページでヘッダーに「Home」リンクが存在しないこと" do
      visit user_path(@user2)
      expect(page).not_to have_selector "#header_home_link"
    end

    scenario "サインイン済のユーザーは、ユーザー詳細ページでヘッダーに「Sign in」リンクが存在しないこと" do
      visit user_path(@user2)
      expect(page).not_to have_selector "#header_sign_in_link"
    end

    scenario "サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Profile」リンクをクリックしたとき、そのユーザーのユーザー詳細ページに遷移すること" do
      visit user_path(@user2)
      click_on :header_profile_link
      expect(current_path).to eq user_path(@user1)
    end

    scenario "サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Sign out」リンクが存在すること" do
      visit user_path(@user2)
      expect(page).to have_selector "#header_sign_out_link"
    end

  end

end
```

ダイレクトアクセスのテストと似ているところが多いですが、新出のコードを紹介していきます！

### page
`expect(page)`という形で現れましたね。これは今表示されているページ全体のことです。

### not_to
`expect().not_to`という表現がでてきましたね。
`to`の反対で`()`と`not_to`以降がアンマッチであることを検証します。
マッチした場合に`false`になり、そのシナリオがFailureになります。

### have_selector
`have_selector`は指定したタグや属性を持っているかを検証します。
例えば、以下のような要素があるとします。

```html
<h1 id="title" class="main-title">Title</h1>
```
これをタグ、`id`属性、`class`属性でそれぞれ`have_selector`で検証しようとすると以下のようになります。

```rb
# タグで検証
expect(page).to have_selector("h1")

# id属性で検証
expect(page).to have_selector("#title")

# class属性で検証
expect(page).to have_selector(".main-title")
```
ページ内や子要素に特定の要素がないかを調べる時に使うので覚えておくとよしです！

### sleep
`sleep`は指定した秒数、次のコードの実行を待つコードです。`sleep 1`であれば1秒待った後に次の行に進みます。
今回は、アプリケーション側でサインイン状態を確認した後リダイレクトする処理を入れていますが、自動化されたテストがそのままのスピードで検証を進めてしまうとリダイレクト処理が終わる前に検証が完了してしまい、思ったとおりの結果を得られないことがあります。
こういった自体を防ぐために、`sleep`を挟むことで処理を待たせることも必要になります。

ただし、`sleep`の使用は最低限にするべきです。なぜならそのせいでテストの実行時間が長くなってしまっては自動化した意味が失われかねないからです。

## 3. ユーザーとして、サインアップしたい
ファイル名は`03_sign_up_spec.rb`でいきます！

```
# touch spec/system/03_sign_up_spec.rb
```

そして、今回も`id`を仕込みます。

```diff:app/views/static_pages/home.html.erb
- <%= link_to "Sign up now!", sign_up_path, class: "btn btn-lg btn-primary mt-5" %>
+ <%= link_to "Sign up now!", sign_up_path, class: "btn btn-lg btn-primary mt-5", id: :sign_up_link %>
```

```diff:app/views/users/new.html.erb
  <%= form_with model: @user, url: create_user_path, local: true do |form| %>
    ...
    <div class="form-group mt-5">
-     <%= form.submit "Sign up!", class: "form-control btn btn-primary" %>
+     <%= form.submit "Sign up!", class: "form-control btn btn-primary", id: :sign_up_button %>
    </div>
  <% end %>
- <p class="text-center">登録済みの方は<%= link_to "こちら", sign_in_path %></p>
+ <p class="text-center">登録済みの方は<%= link_to "こちら", sign_in_path, id: :sign_in_link %></p>
```

はい。ではテストコードです。

```rb:spec/system/03_sign_up_spec.rb
feature "ユーザーとして、サインアップしたい", type: :system do
  background do
    @user = User.new(name: "John Smith", email: "john@sample.com", password: "john1234")
  end

  scenario "未サインインのユーザーが、トップページで「Sign up now!」ボタンを選択したとき、サインアップページに遷移すること" do
    visit root_path
    click_on :sign_up_link
    expect(current_path).to eq sign_up_path
  end

  scenario "サインアップページで「お名前」を入力できること" do
    visit sign_up_path
    fill_in :user_name, with: @user.name
    expect(find("#user_name").value).to eq @user.name
  end

  scenario "サインアップページで「メールアドレス」を入力できること" do
    visit sign_up_path
    fill_in :user_email, with: @user.email
    expect(find("#user_email").value).to eq @user.email
  end

  scenario "サインアップページで「パスワード」を入力できること" do
    visit sign_up_path
    fill_in :user_password, with: @user.password
    expect(find("#user_password").value).to eq @user.password
  end

  scenario "サインアップページで「パスワード」はマスク化されること" do
    visit sign_up_path
    fill_in :user_password, with: @user.password
    expect(find("#user_password")[:type]).to eq "password"
  end

  scenario "サインアップページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスにチェックをいれたとき、「パスワード」が表示されること" do
    visit sign_up_path
    fill_in :user_password, with: @user.password
    check :visible_password
    expect(find("#user_password")[:type]).to eq "text"
  end

  scenario "サインアップページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスのチェックを外したとき、「パスワード」がマスク化されること" do
    visit sign_up_path
    fill_in :user_password, with: @user.password
    check :visible_password
    uncheck :visible_password
    expect(find("#user_password")[:type]).to eq "password"
  end

  scenario "サインアップページで「お名前」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「お名前」未入力のエラーメッセージが表示されること" do
    error_message = "お名前を入力してください"

    visit sign_up_path
    fill_in :user_name, with: ""
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: @user.password
    click_on :sign_up_button
    
    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message
  end

  scenario "サインアップページで「お名前」を51文字以上入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「お名前」文字数超過のエラーメッセージが表示されること" do
    error_message = "お名前は50文字以内で入力してください"

    visit sign_up_path
    fill_in :user_name, with: "a" * 51
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message

    fill_in :user_name, with: "a" * 50
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).not_to eq sign_up_path
    expect(page).not_to have_text error_message
    expect(current_path).to eq user_path(User.find_by(email: @user.email))
  end

  scenario "サインアップページで「メールアドレス」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」未入力のエラーメッセージが表示されること" do
    error_message = "メールアドレスを入力してください"

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: ""
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message
  end

  scenario "サインアップページで「メールアドレス」を256文字以上入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」文字数超過のエラーメッセージが表示されること" do
    error_message = "メールアドレスは255文字以内で入力してください"

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: "a" * 245 + "@sample.com"
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message

    fill_in :user_email, with: "a" * 244 + "@sample.com"
    fill_in :user_password, with: @user.password
    click_on :sign_up_button
    
    expect(current_path).not_to eq sign_up_path
    expect(page).not_to have_text error_message
    expect(current_path).to eq user_path(User.find_by(email: "a" * 244 + "@sample.com")) 
  end

  scenario "サインアップページで「メールアドレス」を誤ったフォーマットで入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」フォーマットチェックエラーのエラーメッセージが表示されること" do
    error_message = "メールアドレスは不正な値です"

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: "sample.com"
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message
  end

  scenario "サインアップページで「メールアドレス」がすでに登録済みのメールアドレスを入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「メールアドレス」重複のエラーメッセージが表示されること" do
    error_message = "メールアドレスはすでに存在します"
    @user.save

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: @user.email.upcase
    fill_in :user_password, with: @user.password
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message
  end

  scenario "サインアップページで「パスワード」を入力していないユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「パスワード」文字数不足のエラーメッセージが表示されること" do
    error_message = "パスワードは6文字以上で入力してください"

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: ""
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message
  end

  scenario "サインアップページで「パスワード」を5文字以下で入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは失敗し「パスワード」文字数不足のエラーメッセージが表示されること" do
    error_message = "パスワードは6文字以上で入力してください"

    visit sign_up_path
    fill_in :user_name, with: @user.name
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: "john1"
    click_on :sign_up_button

    expect(current_path).to eq sign_up_path
    expect(page).to have_text error_message

    fill_in :user_password, with: "john12"
    click_on :sign_up_button

    expect(current_path).not_to eq sign_up_path
    expect(page).not_to have_text error_message
    expect(current_path).to eq user_path(User.find_by(email: @user.email))
  end

  feature nil, type: :system do
    background do
      @welcome_message = "サインアップありがとう"

      visit sign_up_path
      fill_in :user_name, with: @user.name
      fill_in :user_email, with: @user.email
      fill_in :user_password, with: @user.password
      click_on :sign_up_button
    end

    scenario "サインアップページで「お名前」「メールアドレス」「パスワード」を正しく入力したユーザーが、「Sign up!」ボタンをクリックしたとき、サインアップは成功し、そのユーザーのユーザー詳細ページにサインイン済状態で遷移すること" do
      expect(current_path).to eq user_path(User.find_by(email: @user.email))
      expect(page).not_to have_selector "#header_sign_in_link"
      expect(page).to have_selector "#header_sign_out_link"
    end

    scenario "サインアップに成功したユーザーは、遷移後のユーザー詳細ページで自分の入力した「お名前」を確認できること" do
      expect(page).to have_text @user.name
    end

    scenario "サインアップに成功したユーザーは、遷移後のユーザー詳細ページで自分の入力した「メールアドレス」を確認できること" do
      expect(page).to have_text @user.email
    end

    scenario "サインアップに成功したユーザーは、遷移後のユーザー詳細ページでウェルカムメッセージを確認できること" do
      expect(page).to have_text @welcome_message
    end

    scenario "サインアップに成功したユーザーは、遷移後のユーザー詳細ページをリロードしたとき、ウェルカムメッセージを確認できなくなること" do
      visit current_path
      expect(page).not_to have_text @welcome_message
    end
  end

  scenario "サインアップページで「登録済みの方はこちら」リンクを選択したとき、サインインページに遷移すること" do
    visit sign_up_path
    click_on :sign_in_link
    expect(current_path).to eq sign_in_path
  end
end
```
また、はじめましての書き方を紹介していきます。

### find
モデルのときにつかった`find`とはまた別ですよ。
`find()`でページの中から`()`内で指定した要素を取得してくれます。１つ以上該当するものがあるとエラーになってしまうので、`id`属性に対して使うのが好ましいでしょう。
例えば今回のテストコードでは、

```rb
expect(find("#user_name").value).to eq @user.name
```
のように使っていますが、これで`id`属性が`user_name`に定義されている要素を取得します。

### value
```rb
find("#user_name").value
```
のように`value`を使っています。これは`input`に`value`属性を取得しています。
`value`属性には`input type="text"`などの場合にはテキストボックスにデフォルトで入力しておきたい文字列を入力しておいたりしますが、Capybaraでは`value`属性を取得することで今入力されている文字列を取得することができます。

### [:type]
```rb
find("#user_password")[:type]
```
のように使っています。Capybaraでは`value`と`text`は要素と`.`でつなぐことで取得できるのですが、それ以外の属性は`[:attribute_name]`の形式で取得します。`[:type]`だと`type`属性を取得してきていることになりますね。
今回は`password`の`type`属性をjavascriptで`text`と`password`を切り替えているので、これでチェックができます。`text`はマスク化なし、`password`はマスク化ありはHTML5の仕様なので、今回のテストでは`type`属性が正しく指定されているかを検証しました。

### check
`check`はチェックボックスにチェックする操作です。
今回は

```rb
check :visible_password
```
の形式で`id`属性が`visible_password`のチェックボックスにチェックを入れています。

### uncheck
`uncheck`は`check`の反対でチェックボックスからチェックを外す操作です。

### have_text
`have_text`は指定した文字列が存在するかどうかを検証するために使います。

```rb
expect(page).to have_text xxxxxxxxxx
```
と記述することでページのどこかにでも`xxxxxxxxxx`の文字列が存在しないかを検証します。
`page`の箇所を`find()`などで限定した要素にすることで、その要素内に`xxxxxxxxxx`の文字列が存在するかどうかを検証するように範囲を狭めることもできます。

### visit current_path
以前お話したように`current_path`は現在のパスです。そこに`visit`しているということは...

そう！これはリロード操作ですね。

はい。今回のテストコードで新しく出てきた表現はこのくらいではないでしょうか。
そろそろ慣れてきましたか？一回書き始めると案外それらの組み合わせだけでいろいろなテストを実行できることがわかってきたんじゃないかと思います。
それでは次はサインインのテストコードを記述していきましょう！

## 4. ユーザーとして、サインインしたい
まずはシナリオファイルの作成から。

```
# touch spec/system/04_sign_in_spec.rb
```

そして、必要な箇所に`id`属性を振ります。

```diff:app/views/sessions/new.html.erb
- <p class="text-center">登録がまだの方は<%= link_to "こちら", sign_up_path %></p>
+ <p class="text-center">登録がまだの方は<%= link_to "こちら", sign_up_path, id: :sign_up_link %></p>
```

以下、テストコードです。今回は目新しい表現はないので、下のコードを見ずに書いてみてもらっても面白いかもしれないです。

```rb:spec/system/04_sign_in_spec.rb
feature "ユーザーとして、サインインしたい", type: :system do
  background do
    @user = User.create(name: "John Smith", email: "john@sample.com", password: "john1234")
  end

  scenario "サインインページで「メールアドレス」を入力できること" do
    visit sign_in_path
    fill_in :user_email, with: @user.email
    expect(find("#user_email").value).to eq @user.email
  end

  scenario "サインインページで「パスワード」を入力できること" do
    visit sign_in_path
    fill_in :user_password, with: @user.password
    expect(find("#user_password").value).to eq @user.password
  end

  scenario "サインインページで「パスワード」はマスク化されること" do
    visit sign_in_path
    fill_in :user_password, with: @user.password
    expect(find("#user_password")[:type]).to eq "password"
  end

  scenario "サインインページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスにチェックをいれたとき、「パスワード」が表示されること" do
    visit sign_in_path
    fill_in :user_password, with: @user.password
    check :visible_password
    expect(find("#user_password")[:type]).to eq "text"
  end

  scenario "サインインページで「パスワード」を入力したユーザーが、「パスワードを表示する」チェックボックスのチェックを外したとき、「パスワード」がマスク化されること" do
    visit sign_in_path
    fill_in :user_password, with: @user.password
    check :visible_password
    uncheck :visible_password
    expect(find("#user_password")[:type]).to eq "password"
  end

  scenario "サインインページで「メールアドレス」を入力していないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること" do
    error_message = "メールアドレスまたはパスワードをもう一度確認してください。"

    visit sign_in_path
    fill_in :user_email, with: ""
    fill_in :user_password, with: @user.password
    click_on :sign_in_button

    expect(current_path).to eq sign_in_path
    expect(page).to have_text error_message
  end

  scenario "サインインページで「メールアドレス」として登録されていないメールアドレスを入力したユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること" do
    error_message = "メールアドレスまたはパスワードをもう一度確認してください。"

    visit sign_in_path
    fill_in :user_email, with: "dummy@sample.com"
    fill_in :user_password, with: @user.password
    click_on :sign_in_button

    expect(current_path).to eq sign_in_path
    expect(page).to have_text error_message
  end

  scenario "サインインページで「メールアドレス」は正しいが「パスワード」を入力していないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること" do
    error_message = "メールアドレスまたはパスワードをもう一度確認してください。"

    visit sign_in_path
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: ""
    click_on :sign_in_button

    expect(current_path).to eq sign_in_path
    expect(page).to have_text error_message  
  end

  scenario "サインインページで「メールアドレス」は正しいが「パスワード」が正しくないユーザーが、「Sign in」ボタンをクリックしたとき、サインイン失敗のエラーメッセージが表示されること" do
    error_message = "メールアドレスまたはパスワードをもう一度確認してください。"

    visit sign_in_path
    fill_in :user_email, with: @user.email
    fill_in :user_password, with: @user.password + "a"
    click_on :sign_in_button

    expect(current_path).to eq sign_in_path
    expect(page).to have_text error_message      
  end

  feature nil, type: :system do
    background do
      @sign_in_message = "サインインしました。"

      visit sign_in_path
      fill_in :user_email, with: @user.email
      fill_in :user_password, with: @user.password
      click_on :sign_in_button        
    end
    
    scenario "サインインページで「メールアドレス」「パスワード」に正しい値を入力したユーザーが、「Sign in」ボタンをクリックしたとき、サインイン済状態でそのユーザーのユーザー詳細ページに遷移すること" do
      expect(current_path).to eq user_path(@user)
      expect(page).not_to have_selector "#header_sign_in_link"
      expect(page).to have_selector "#header_sign_out_link"
    end
  
    scenario "サインインに成功したユーザーは、遷移後のユーザー詳細ページでサインイン成功メッセージを確認できること" do
      expect(page).to have_text @sign_in_message
    end

    scenario "サインインに成功したユーザーは、遷移後のユーザー詳細ページをリロードしたとき、サインイン成功メッセージを確認できなくなること" do
      visit current_path
      expect(page).not_to have_text @sign_in_message
    end
  end

  scenario "サインインページで「登録がまだの方はこちら」リンクを選択したとき、サインアップページに遷移すること" do
    visit sign_in_path
    click_on :sign_up_link

    expect(current_path).to eq sign_up_path
  end
end
```

## 5. ユーザーとして、サインアウトしたい
次はサインアウトについてですね。

```
# touch spec/system/05_sign_out_spec.rb
```

そしてコーディング。これも今までのコードの組み合わせで表現可能ですね。

```rb:spec/system/05_sign_out_spec.rb
feature "ユーザーとして、サインアウトしたい", type: :system do
  scenario "サインイン済のユーザーが、ユーザー詳細ページでヘッダーの「Sign out」リンクをクリックしたとき、未サインイン状態になりトップページに遷移すること" do
    user = User.create(name: "John Smith", email: "john@sample.com", password: "john1234")

    visit sign_in_path
    fill_in :user_email, with: user.email
    fill_in :user_password, with: user.password
    click_on :sign_in_button

    visit user_path(user)

    click_on :header_sign_out_link

    expect(current_path).to eq root_path
    expect(page).to have_selector "#header_sign_in_link"
    expect(page).not_to have_selector "#header_sign_out_link"
  end
end
```
コメントアウトなどで説明文を書いたりは省いていますが、今までの内容が理解できていれば何をしているのか想像できると思います。
サインインページにアクセスしてサインインし、ユーザー詳細ページでヘッダーのサインアウトリンクをクリックし、トップページにリダイレクトされたと同時に未サインイン状態になっていることを検証していますね。

## 6. ユーザーとして、他のユーザーの情報を閲覧したい
まずはシナリオファイルです。

```
# touch spec/system/06_show_user_info_spec.rb
```
今回のテストでは、NotFoundのユーザーのユーザー詳細ページを表示しようとした時に、NotFoundのページが表示される、という項目があります。
Railsでは、NotFoundの例外が発生した場合、`production`環境の場合はデフォルトでNotFound用のページが表示されるようになっています。
`test`環境でも同じようにNotFoundページが表示されるようにconfigを変更します。

```diff:config/environments/test.rb
- config.consider_all_requests_local       = true
+ config.consider_all_requests_local       = false
- config.action_dispatch.show_exceptions = false
+ config.action_dispatch.show_exceptions = true
```
これで準備完了です。
NotFoundの場合、`public/404.html`が表示されるようになります。
中身を見ると、

```html
<h1>The page you were looking for doesn't exist.</h1>
```
と記述されているので、この文字列があるかどうかをチェックするようにします。

ではテストコードです。

```rb:spec/system/06_show_user_info_spec.rb
feature "ユーザーとして、他のユーザーの情報を閲覧したい", type: :system do
  background do
    @user1 = User.create(name: "John Smith", email: "john@sample.com", password: "john1234")
    @user2 = User.create(name: "Taro Yamada", email: "taro@sample.com", password: "taro1234")    
  end
  
  scenario "ユーザーが、存在するユーザーのユーザー詳細ページにアクセスしようとしたとき、そのユーザーの「お名前」「メールアドレス」を確認できること" do

    # Before sign in
    visit user_path(@user1)
    expect(page).to have_text @user1.name
    expect(page).to have_text @user1.email
    expect(page).not_to have_text @user2.name
    expect(page).not_to have_text @user2.email

    visit user_path(@user2)
    expect(page).not_to have_text @user1.name
    expect(page).not_to have_text @user1.email
    expect(page).to have_text @user2.name
    expect(page).to have_text @user2.email

    # After sign in
    visit sign_in_path
    fill_in :user_email, with: @user1.email
    fill_in :user_password, with: @user1.password
    click_on :sign_in_button

    visit user_path(@user1)
    expect(page).to have_text @user1.name
    expect(page).to have_text @user1.email
    expect(page).not_to have_text @user2.name
    expect(page).not_to have_text @user2.email

    visit user_path(@user2)
    expect(page).not_to have_text @user1.name
    expect(page).not_to have_text @user1.email
    expect(page).to have_text @user2.name
    expect(page).to have_text @user2.email
  end

  scenario "ユーザーが、存在しないユーザーのユーザー詳細ページにアクセスしようとしたとき、エラーが発生すること" do
    not_found_message = "The page you were looking for doesn't exist."
    not_found_id = @user2.id + 1
    expect{User.find(not_found_id)}.to raise_exception(ActiveRecord::RecordNotFound)

    # Before sign in
    visit user_path(not_found_id)
    expect(page).to have_text not_found_message

    # After sign in
    visit sign_in_path
    fill_in :user_email, with: @user1.email
    fill_in :user_password, with: @user1.password
    click_on :sign_in_button

    visit user_path(not_found_id)
    expect(page).to have_text not_found_message
  end
end
```
基本的には今までと変わりありませんね。
一つだけexceptionの検証の仕方だけ新出があるのでそれの説明を。

### expect{}.to raise_exception()
今までと違うのは`expect`の検証ターゲットを`()`ではなく`{}`でかこっていることですね。
そして`raise_exception`の後に期待する例外を記述します。
今回は`@user2`の`id`に`+1`した`id`のユーザーを検索しています。`id`はシーケンシャルに払い出されるので`@user2`よりも大きい`id`を持っているユーザーはいないはず。
なので`User.find(not_found_id)`は`ActiveRecord::RecordNotFound`の例外が発生するはずです。

はい。ここまでで今のところ考えられる全てのテストケースをコーディングしてみました。
テストを実行してみましょう！

```
# rspec
Capybara starting Puma...
* Version 4.3.1 , codename: Mysterious Traveller
* Min threads: 0, max threads: 4
* Listening on tcp://127.0.0.1:46237
......................................................................

Finished in 1 minute 21.27 seconds (files took 5.82 seconds to load)
70 examples, 0 failures
```
全てパスしてますね！
もしパスしないテストケースがある場合は、もう一度アプリのコードかテストコードを見返してみてくださいね。

また、あえてエラーになるようにテストコードを書き直してみてエラーになることを確認してみるのも面白いと思います。


さて、では本日はここまでにしましょう！

# まとめ
今日は今まで作ってきたアプリに対してテストコードをコーディングしてみました。
これによって今後リファクタリングの都度自動テストを回すだけで全ての動作を確認することができるようになりましたね。

テスト自動化、楽しいですよね？？

テスト自動化は新しい機能を作ったり、アプリの仕様自体を変更する時にテストコードも記述する必要があるのでその分稼働が必要になることもあります。
しかし、リファクタリングや新機能開発時のデグレテストを簡略化でき、自動テストをパスしていればデプロイを自信をもって行える安心感を得ることができます。特にアプリが大きくなっていくと、これは稼働以上に嬉しい恩恵です。

今後はこのハンズオンでもTDDで開発を進めていきます！

では、次回も乞うご期待！ここまでお読みいただきありがとうございました！

Next: Coming Soon

# 後片付け
では後片付けしていきますー。

前回もお話した通り、RSpecのシステムテストはテストが終わるとDBを勝手にリセットしてくれます。
ので、コンテナを落として終了ですね。

```
# exit
```
```
$ docker-compose down
```

# 本日のソースコード
- https://github.com/at946/rails_on_docker_handson/tree/vol.11

# Reference
- [【Rails】こわくない！TDD/BDD・テスト自動化はじめの一歩ハンズオン！ - Qiita](https://qiita.com/at-946/items/45438562042e3b2643e7)
- [使えるRSpec入門・その4「どんなブラウザ操作も自由自在！逆引きCapybara大辞典」 - Qiita](https://qiita.com/jnchito/items/607f956263c38a5fec24)
- [Class: Capybara::Node::Element — Documentation for jnicklas/capybara (master)](https://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Element)
- [【FeatureSpec】404 / 500ページを表示させる - Qiita](https://qiita.com/noriya1217/items/911507365d9d0bc7b107)

# Other Hands-on Links
- https://github.com/at946/rails_on_docker_handson/tree/master