LoginSignup
2
4

More than 3 years have passed since last update.

[RSpec] ぼくが既存のプロダクトコードにテストを書く際の慣例

Last updated at Posted at 2017-06-01

概要

表題のシーンにて書き方が自分の中で定型化しているので公開してみるのです。

対象となる読者

  • これからRSpecを学びたい人
  • RSpecでテストをモリモリ書いている人

環境

  • ruby 2.2.2p95
  • RSpec 3.5.4

既存modelのテストを書く

まずプロダクトコードを眺める

app/models/user.rb
class User
  include ActiveModel::Model

  attr_accessor :first_name, :last_name

  def fullname_with(delimiter)
    return 'そうじゃない' if ちがう?
    return '雅之じゃない!' if そうじゃない?
    "#{@last_name}#{delimiter}#{@first_name}"
  end

  private

  def ちがう?
    @first_name == 'ちがう'
  end

  def そうじゃない?
    @first_name == 'ちがうちがう'
  end
end

describeを列挙する

spec/models/user_spec.rb
require 'rails_helper'

describe User, type: :model do
  xdescribe '#fullname_with'
end

ちょっと脱線しますが、ここでコメントを [タスクやストーリーのID] :shirt: describe列挙のみ などとしてcommitを別けます。privateメソッドのテストも書くかはプロジェクトの方針によりますが、ここでは書かない方針で進めさせてください。

メソッドの入出力を意識して簡単なテストを書く

spec/models/user_spec.rb
require 'rails_helper'

describe User, type: :model do
  describe '#fullname_with' do
    let(:delimiter) {  } # メソッドに渡す入力
    subject { described_class.new.fullname_with(delimiter) } # 何をテストしているのかを明示
    it { is_expected.to be_nil } # 期待する出力
  end
end

ポイント

  • このitは通りませんが リズムに乗りたいのでここまで一気に書き上げます。 自分の場合はATOM Editorのプラグインruby-testのショートカットキーでテストを即時実行しているのでスピード感が出せています。
  • described_classは使わない方が可読性が高いと思うのですが、ちょっと格好付けたい場合に気まぐれで書いてます(格好良いかのツッコミは無しで)。
  • 以前は強調したいという意図からsubjectを一行で書ける場合もdo〜endで複数行にしていましたが、コードが縦長になるほうが嫌だなと思うようになり現在は上記のようにしています。
  • itに注釈を書かないようにしています。書くとしても処理内容を自然語に翻訳するだけになるので冗長だと思うからです。すると、テストの内容が実行結果から解るようにするため、おのずとitは一行にするのが望ましくなります。
    • ただし複数のitを書くことによりテストの実行時間が著しく低下する場合はこの限りではなく、itに複数行の処理を書いた上で「要は何をチェックしてるの?」を注釈文として記載します。
expected: nil
     got: ""

ここまで視認して、残念なTYPOなど無いことが解るので「さ〜てモリモリ書くぞぉ」という決意をします。

場合分けのテストを追加する

具体的にはcontextを追記します。contextには注釈を記載します。

spec/models/user_spec.rb
require 'rails_helper'

describe User, type: :model do
  describe '#fullname_with' do
    let(:user) { described_class.new }
    subject { user.fullname_with(delimiter) }

    context '引数の区切り文字に値がある' do
      let(:delimiter) { '-' }
      it { is_expected.to eq '-' }
      context '姓が`まつやま`、名が `ひでゆき`' do
        before { user.first_name = 'ひでゆき' }
        before { user.last_name = 'まつやま' }
        it { is_expected.to eq 'まつやま-ひでゆき' }
      end
      context '#ちがう?が真' do
        before { expect_any_instance_of(described_class).to receive(:ちがう?) { true } }
        it { is_expected.to eq 'そうじゃない' }
      end
      context '#そうじゃない?が真' do
        before { expect_any_instance_of(described_class).to receive(:そうじゃない?) { true } }
        it { is_expected.to eq '雅之じゃない!' }
      end
    end
    context '引数の区切り文字に値がない' do
      let(:delimiter) {  }
      it { is_expected.to be_blank }
    end
  end
end
User
  #fullname_with

    引数の区切り文字に値がある
      should eq "-"
      姓が`まつやま`、名が `ひでゆき`
        should eq "まつやま-ひでゆき"
      #ちがう?が真
        should eq "そうじゃない"
      #そうじゃない?が真
        should eq "雅之じゃない!"
    引数の区切り文字に値がない
      should be blank

Finished in 0.01258 seconds (files took 0.23769 seconds to load)
5 examples, 0 failures

ポイント

  • 区切り文字がハイフンかどうかの判断分岐は実装されていないので、そのテストは書きません。
  • 区切り文字に値がない場合 も雅之じゃないを書きたい気持ちが湧きますが、やはり分岐が実装されていないのでテストを書きません。
  • #fullname_with のテストなので ちがう?そうじゃない? の処理にはあえて触れていません。必要に応じて別途、それぞれのテストを書きます。

まとめ

冒頭で書いた、

spec/models/user_spec.rb
require 'rails_helper'

describe User, type: :model do
  describe '#fullname_with' do
    let(:delimiter) {  } # メソッドに渡す入力
    subject { described_class.new.fullname_with(delimiter) } # 何をテストしているのかを明示
    it { is_expected.to be_nil } # 期待する出力
  end
end

ですが、このテンプレでテストが簡単に書けないメソッドは複雑になっていると思います。本稿では既存のコードにテストを書くというものでしたが、最も言いたかったことは 最初からテストを書くことでメンテナンスコストを抑えれるプロダクトコードを作ろうぜい ということでした。最初からテストを書いていれば、後の技術的負債を抑えられるコードが書けると思うのです。それこそrubocop先生のABCsizeチェックを無効にするようなメソッドが初めから生まれない、と考えています。

関連記事

[RSpec] テストが書きやすいコードは良いコード

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4