概要
表題のシーンにて書き方が自分の中で定型化しているので公開してみるのです。
対象となる読者
- これから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] 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チェックを無効にするようなメソッドが初めから生まれない、と考えています。