テストコード嫌いを無くすコツ

## はじめに  
どうも、どうもどうも、ツクリンクでRailsエンジニアをしている @akaryu0206 です。  
こちらの記事は[ツクリンク プロダクト部 Advent Calendar 2023](https://qiita.com/advent-calendar/2023/tsukulink)、8日目の記事です。  
普段記事を書く機会がなく（苦手なだけ）、克服したいなーと思っているところでもあったので記事を書くことにしました。  
  
## テストコードについて  
と、言うことで今回はテストコードについて書いてみようと思います。  
正直なところ、テストコードは「なくてもいい」ものではあるので、私がこれまでお仕事をさせて頂いた企業でも全く書かないところもあれば徹底的に書くところもあり、チーム文化次第といったところでした。  
弊社はテストコードは必ず書く文化ですが、チームにテストコードを書くのが苦手と言うメンバーがいたので私なりのコツをアドバイスしたところ意外と好評だったのでそれを紹介しようと思います。  
  
なお、この記事ではRspecを用いたテストコードについて解説しています。  
  
## テストコードを書く際のポイント  
私がテストコードを書く際に意識している主なポイントは下記の4つです。  
  
1. describe, contextを適切に使う  
2. テストケースは日本語で書く  
3. テストケースの文章にある程度制約を設ける  
4. 実装前にテストケースを書き出す  
### 1. describe, contextを適切に使う  
Rspec系の解説記事でよく見かけると思いますが、describeやcontextを適切に使ってテストケースを分割します。  
モデルスペックの場合、describeはテスト対象のメソッド名、contextはテスト対象の条件、みたいな感じですね。  
リクエストスペックの場合はdescribeがテスト対象のエンドポイントになります。  
  
```ruby
RSpec.describe User do
  # Userモデルのactive?メソッドのテスト
  describe '#active?' do
    # active?メソッドをテストする時の条件1
    context '最終ログイン日時が24時間未満の場合' do
      example '真を返すこと'
    end

    # active?メソッドをテストする時の条件2
    context '最終ログイン日時が24時間を超過する場合' do
      example '偽を返すこと'
    end
  end
end
```  
  
### 2. context, exampleは日本語で書く  
これはまぁ「無理して英語で書かなくても書きやすい日本語でいいよね」ってことですね。  
日本語が不得意な外国籍の方がいるチームや「テストケースは英語で書くこと」みたいなルールがあるチームでなければ逆に「テストケースは日本語で書くこと」というルールを設けてもいいのではないでしょうか。  
ちなみに、私が日本語でテストケースを書く時はなるべく違和感をなくすため、itでなくexampleを使うようにしています。  

```ruby
RSpec.describe User do
  describe '#active?' do
    # 日本語のほうが書きやすいし、他の人も読みやすい
    context '最終ログイン日時が24時間未満の場合' do
      example '真を返すこと'
    end

    # 英語だと書く人も読む人も英語力を要求される
    context 'if last logged in less than 24 hours' do
      it 'is return false'
    end
  end
end
```
  
### 3. テストケースの文章にある程度制約を設ける  
メソッドの命名規則などに若干通ずるものがあるのですが、contextの場合は末尾が「〜の場合」、exampleの場合は末尾が「〜こと」で終わるように書くようにします。  
contextとexampleを繋げた時にテスト仕様書にそのままコピペできるような文章になるイメージですね。
これは私が昔Rspecのdescribe,context,exampleをテキストで出力して非エンジニアに仕様書代わりに渡していた経緯があったためですが、このルールに則ってテストケースを書くことで文章の書き方が制限されメンバー間の日本語のクセやcontextやexampleに書くことを統一できます。  

```ruby
RSpec.describe User do
  describe '#active?' do
    # contextとexampleを繋げると「最終ログイン日時が24時間未満の場合真を返すこと」となる
    # 日本語としての違和感が無い(contextがネストしても違和感を少なくできる)
    context '最終ログイン日時が24時間未満の場合' do
      example '真を返すこと'
    end

    # contextとexampleを繋げると「24時間以内にログインしたユーザ、真が返る」となる
    # 日本語としての違和感が、、、
    context '24時間以内にログインしたユーザ' do
      it '真が返る'
    end
  end
end
```
  
### 4. 実装前にテストケースを書き出す  
改修や修正、機能追加を行う時は必ずその実装を行う前に仕様の確認を行いますよね。  
その時にFIXされた仕様をテストケースとして先に書き出しておけば、仕様書にもなるしテスト仕様書にもなるので便利！という考えです。  
注意点として、この時に書き出すのはdescribe, context, exampleのブロックだけでテストコード自体はまだ書きません。  
  
TDDでもないしテストファーストでもない、テストケースファーストとでも呼ぶのがいいかもしれません。  
もちろん、テストケースを書き出したあとは先にテストコードでも先に実装でもどちらから行ってもOKです。

```ruby
RSpec.describe User do
  describe '#active?' do
    context '最終ログイン日時が24時間未満の場合' do
      example '真を返すこと'
    end

    context '最終ログイン日時が24時間を超過する場合' do
      example '偽を返すこと'
    end
  end

  describe '#fullname'
    context '姓、名が設定されている場合' do
      example '姓、名の順番で結合された文字列を返すこと'
    end

    context '姓が設定されていない場合' do
      # 最初の段階で具体的にどういったエラーを発生させるかまでは書かなくてもいい
      example 'エラーが発生すること'
    end

    context '名が設定されていない場合' do
      example 'エラーが発生すること'
    end
  end

  describe '#adult?' do
    context '設定されている生年月日が現在日時より18年以上の場合' do
      example '真を返すこと'
    end

    context '設定されている生年月日が現在日時より18年未満の場合' do
      example '偽を返すこと'
    end
  end
end
```

## さいごに
いかがでしょうか？
今回紹介した内容はテクニックというよりはどちらかというと規則に近いものですが、テストケースさえスラスラと書けるようになってしまえば(テストすべきことが明確になっていれば)テストコードを書くこと自体はそこまで難しくないかもしれません。
もしこの記事がみなさんのテストコードに対する苦手意識を減らす手助けになれば幸いです。