LoginSignup
1
1

More than 5 years have passed since last update.

カバレッジ100%を目指す

Last updated at Posted at 2015-12-08

オレオレルールを晒す第二弾。

ソースコード内のどの程度の割合を自動テスト出来るか、という指標を、テストカバレッジといいます。カバレッジの定義を真面目にやろうとするとC0とかC1とかいろいろ出てきますが、ここでは「とりあえず一通り動かす」という雑な理解で話を進めます。

ruby の弱点

よく言われる ruby の弱点の1つとして、「動的言語なので、他の言語ではコンパイル時にわかるエラーが事前に発見できない」というものがあります。
例えば以下のコードを御覧ください。

def edit
  @post = Post.find(params[:id])
  raise RestfulError::Forbiden unless @post.user_id == current_user.id
end

RSpec.describe 'edit' do
  subject { get :edit, id: post.id, session }
  let(:post){ FactoryGirl.create(:post, user_id: user.id) }
  it { is_exptected.to render_template('edit') }
end

編集しようとする post が自分の post じゃなければ例外を投げる、という処理です。テストを雑に用意しましたが、このテストは raise RestfulError::Forbiden の部分を通りません。
実は Forbiden の部分でスペルミスをしているのですが、 ruby は動的言語ゆえ、実際にスペルミスの箇所を実行しない限り、エラーが起きません。 Java や C のようなコンパイル畑からやってきた方には「これだからrubyちゃんは信用できないのよね」と陰口を叩かれがちです。

そこで。
自動テストで「とりあえず全てのコード片を一通り評価させる」というのを目指すわけです。

(ちなみに RestfulError は拙作の gem です。ぜひご利用ください!)

条件分岐にそって context を書く

状況によって挙動が変わるメソッドのテストを書くときには、取りうる値を全部テストせよとか境界条件をテストせよとかこれもいろいろ理論があるのですが、とりあえず置いといて、ここではとにかく「カバレッジ100%を目指す」ことだけを考えます。

上述のコードを、条件分岐にそって context を書くと以下のようになります。

RSpec.describe 'edit' do
  subject { get :edit, id: post.id, session }
  let(:post){ FactoryGirl.create(:post, user_id: post_user_id) }
  context '@post.user_id == current_user.id'
    let(:post_user_id){ user.id }
    it { is_exptected.to render_template('edit') }
  end
  context '@post.user_id != current_user.id'
    let(:post_user_id){ 0 }
    it { exptect{subject}.to raise_error }
  end
end

(describe の次行に subject を、 context の次行に let を書く も参照ください)

もう少し複雑に条件分岐している例はこちら。条件分岐にそって context を書くようにすると、構造化のルールが一定し、100%カバーテストを記述しやすくなります。

def fizzbuzz(num)
  if num % 3 == 0
    ret = 'Fizz'
    if num % 5 == 0
      ret << 'Buzz'
    end
  elsif num % 5 == 0
    ret = 'Buzz'
  else
    ret = num.to_s
  end
  ret
end

RSpec.describe 'fizzbuzz' do
  subject { fizzbuzz(num) }
  context 'num % 3 == 0' do
    let(:num){ 3 }
    it { is_expected.to eq 'Fizz' }
    context '&& num % 5 == 0' do
      let(:num){ 15 }
      it { is_expected.to eq 'FizzBuzz' }
    end
  end
  context 'num % 3 != 0 && num % 5 == 0' do
    let(:num){ 5 }
    it { is_expected.to eq 'Buzz' }
  end
  context 'num % 3 != 0 && num % 5 != 0' do
    let(:num){ 4 }
    it { is_expected.to eq '4' }
  end
end
1
1
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
1
1