Ruby on RailsのテストフレームワークRspecでコードカバレッジを理解する


テストケースを書いていると、どこまでカバレッジをカバーするって話がでてきます。

コードカバレッジってそういえばどんなのだっけ?って思ったので、自分の備忘録としてまとめようと思います。

コードカバレッジとは?

ソフトウェアテストにおいて、テスト対象となるプログラムコード(内部ロジック)全
体の中で、テストが行われた部分が占める割合(網羅率)です。
参照

代表的なカバレッジ

C0:命令網羅

それぞれの命令文が少なくとも1回は実行

C1:分岐網羅

それぞれの判定条件に置ける真偽が少なとも1回は実行

C2:条件網羅

それぞれの条件文に置ける真偽が少なくとも1回は実行

MCC:複合条件網羅

それぞれの条件に置ける真偽の組み合わせが全て実行

実際Rspecでカバレッジを意識してテストを書く

テストをするRubyコード

sample.rb
module CodeCoverage
  class << self
  # main
  # @params [interger] x
  # @params [interger] y
  # @return [String] xとyの値によってことなる文字列
  #
  # 引数は、intergerのみを想定
  def main(x, y)
    result = ''
    if x >= y || x % 2 == 0
      result << '処理1'
    else
      result << '処理2'
    end
    if x*y > 10
      result << '処理3'
    else
      result << '処理4'
    end
    return result
  end
  end
end

Rspecで書いたテストコード

Rspecとは

Ruby on Railsの開発手法の1つであるTDD(テスト駆動開発)を実現するためのフレームワークの1つ
参考

テストコード本体

RSpec.describe CodeCoverage do
  shared_examples '処理1と処理3通る' do
    it '処理1と処理3の文言が返却される' do
      expect(subject).to eq '処理1処理3'
    end
  end
  shared_examples '処理1と処理4通る' do
    it '処理1と処理4の文言が返却される' do
      expect(subject).to eq '処理1処理4'
    end
  end
  shared_examples '処理2と処理3通る' do
    it '処理2と処理3の文言が返却される' do
      expect(subject).to eq '処理2処理3'
    end
  end
  shared_examples '処理2と処理4通る' do
    it '処理2と処理4の文言が返却される' do
      expect(subject).to eq '処理2処理4'
    end
  end

  describe 'main' do
    subject { described_class.send(:main, x, y) }
      # C0:命令網羅
    # 命令が少なくても1回だけ実行するように書く
      context 'C0の場合' do
        context '処理1と処理3を通る' do
          let(:x) { 11 }
          let(:y) { 1 }
          it_behaves_like '処理1と処理3通る'
        end
        context '処理2と処理4を通る' do
          let(:x) { 1 }
          let(:y) { 9 }
          it_behaves_like '処理2と処理4通る'
        end
      end
      # C1:分岐網羅
      # それぞれの判定条件に置ける真偽が少なとも1回は実行
      context 'C1の場合' do
        context '処理1と処理3を通る' do
          let(:x) { 11 }
          let(:y) { 1 }
          it_behaves_like '処理1と処理3通る'
        end
        context '処理1と処理4を通る' do
          let(:x) { 3 }
          let(:y) { 2 }
          it_behaves_like '処理1と処理4通る'
        end
        context '処理2と処理3を通る' do
          let(:x) { 3 }
          let(:y) { 5 }
          it_behaves_like '処理2と処理3通る'
        end
        context '処理2と処理4を通る' do
          let(:x) { 1 }
          let(:y) { 4 }
          it_behaves_like '処理2と処理4通る'
        end
      end

      # C2:条件網羅
      # それぞれの条件文に置ける真偽が少なくとも1回は実行
      context 'C2の場合' do
        context 'x>=yかつx*y>10' do
          context '処理1と処理3を通る' do
            let(:x) { 11 }
            let(:y) { 1 }
            it_behaves_like '処理1と処理3通る'
          end
        end
        context 'x>=yかつx*y<10' do
          context '処理1と処理4を通る' do
            let(:x) { 3 }
            let(:y) { 2 }
            it_behaves_like '処理1と処理4通る'
          end
        end
        context 'x<yかつxが偶数かつx*y>10' do
          context '処理1と処理3を通る' do
            let(:x) { 4 }
       let(:y) { 5 }
            it_behaves_like '処理1と処理3通る'
          end
        end
        context 'x<yかつxが偶数かつx*y<10' do
          context '処理1と処理4を通る' do
            let(:x) { 2 }
            let(:y) { 3 }
            it_behaves_like '処理1と処理4通る'
          end
        end
        context 'x<yかつxが奇数かつx*y>10' do
          context '処理2と処理3を通る' do
            let(:x) { 3 }
            let(:y) { 5 }
            it_behaves_like '処理2と処理3通る'
          end
        end
        context 'x<yかつxが奇数かつx*y<10' do
          context '処理2と処理4を通る' do
            let(:x) { 1 }
            let(:y) { 4 }
            it_behaves_like '処理2と処理4通る'
          end
        end
      end
    end
  end
end

こうやって書くとパッとわかりやすいですね。
どこまでのコードカバレッジを満たすかは、適宜ですが、仕事だとC1ぐらいまでです。