RSpecによるテスト記述
はじめに
RSpec2の書き方について,考えがまとまってきたので残しておきます.
テスト作成における全体流れとしては,
- メソッドレベルでテストケースを作成
- 1.をRSpecに落としこむ
ただし,1.におけるテストケースは2.に向けて書きます.
1. メソッドレベルでテストケースを作成
具体的には,下のようなテストケース表を作ります.
#ケース | テストケース名 | 入力1:input_1 | 出力 | 例外 |
---|---|---|---|---|
1 | 正常系:〜のとき,〜を返却すること | |||
2 | 異常系:〜のとき,〜の例外を投げること | 例外名, 例外メッセージ |
具体例を考えてみます.
例えば,以下のようなクラスに対するテストケースを考えてみます.
# -*- mode:ruby; coding:utf-8 -*- class ClassName def self.method_name(input_1, input_2) return input_1 + input_2 end end
#ケース | テストケース名 | 入力1:input_1 | 入力2:input_2 | 出力 | 例外 |
---|---|---|---|---|---|
1 | 正常系:入力値がともに文字列であるとき,それらを連結した文字列を返却すること | 'hoge' | 'fuga' | 'hogefuga' | |
2 | 異常系:input_1が文字列以外であるとき,例外を投げること | nil | 'fuga' | ArgumentError, '入力値が不正です.input_1が文字列以外です.' | |
3 | 異常系:input_2が文字列以外であるとき,例外を投げること | 'hoge' | 3 | ArgumentError, '入力値が不正です.input_2が文字列以外です.' |
テストケースの作成方法については,ここでは要因分析*1に基いて考えています.
2. テストケースをRSpecに落としこむ
1.で作成したテストケースを,以下のフォーマットに埋め込む形でテストコードを作成します.
# -*- mode:ruby; coding:utf-8 -*- require './test.rb' # テスト対象のファイルの相対パス describe ClassName do describe "method_name" do context '正常系' do it '〜のとき,〜を返却すること' do # 入力引数 input = 入力値 # 出力 result = 出力値 # テスト expect( ClassName.method_name(input) ).to eq(result) end end context '異常系' do it '〜のとき,〜の例外を投げること' do # 入力引数 input = 入力値 # テスト expect{ ClassName.method_name(input) }.to raise_error(例外名, '例外メッセージ') end end end end
1.で考えた具体例について,RSpecを作成してみます.
これは,ほぼコピペ作業で済みます.
# -*- mode:ruby; coding:utf-8 -*- require './test.rb' # テスト対象のファイルの相対パス describe ClassName do describe ".method_name" do context "正常系" do it '入力値がともに文字列であるとき,それらを連結した文字列を返却すること' do # 入力引数 input_1 = 'hoge' input_2 = 'fuga' # 出力 result = 'hogefuga' # テスト expect( ClassName.method_name(input_1, input_2) ).to eq(result) end end context '異常系' do it 'input_1が文字列以外であるとき,例外を投げること' do # 入力引数 input_1 = nil input_2 = 'fuga' # テスト expect{ ClassName.method_name(input_1, input_2) }.to raise_error(ArgumentError, '入力値が不正です.input_1が文字列以外です.') end it 'input_2が文字列以外であるとき,例外を投げること' do # 入力引数 input_1 = 'hoge' input_2 = 3 # テスト expect{ ClassName.method_name(input_1, input_2) }.to raise_error(ArgumentError, '入力値が不正です.input_2が文字列以外です.') end end end end
今の状態(例外処理を加えていない状態)でrspecを実行すると次のようになります.
% rspec test_spec.rb -fs [~/ruby/rspec] ClassName .method_name 正常系 入力値がともに文字列であるとき,それらを連結した文字列を返却すること 異常系 input_1が文字列以外であるとき,例外を投げること (FAILED - 1) input_2が文字列以外であるとき,例外を投げること (FAILED - 2) Failures: 1) ClassName.method_name 異常系 input_1が文字列以外であるとき,例外を投げること Failure/Error: expect{ ClassName.method_name(input_1, input_2) }.to raise_error(ArgumentError, '入力値が不正です.input_1が文字列以外です.') expected ArgumentError with "入力値が不正です.input_1が文字列以外です.", got #<NoMethodError: undefined method `+' for nil:NilClass> with backtrace: # ./test.rb:7:in `method_name' # ./test_spec.rb:25:in `block (5 levels) in <top (required)>' # ./test_spec.rb:25:in `block (4 levels) in <top (required)>' # ./test_spec.rb:25:in `block (4 levels) in <top (required)>' 2) ClassName.method_name 異常系 input_2が文字列以外であるとき,例外を投げること Failure/Error: expect{ ClassName.method_name(input_1, input_2) }.to raise_error(ArgumentError, '入力値が不正です.input_2が文字列以外です.') expected ArgumentError with "入力値が不正です.input_2が文字列以外です.", got #<TypeError: can't convert Fixnum into String> with backtrace: # ./test.rb:7:in `+' # ./test.rb:7:in `method_name' # ./test_spec.rb:33:in `block (5 levels) in <top (required)>' # ./test_spec.rb:33:in `block (4 levels) in <top (required)>' # ./test_spec.rb:33:in `block (4 levels) in <top (required)>' Finished in 0.00495 seconds 3 examples, 2 failures Failed examples: rspec ./test_spec.rb:20 # ClassName.method_name 異常系 input_1が文字列以外であるとき,例外を投げること rspec ./test_spec.rb:28 # ClassName.method_name 異常系 input_2が文字列以外であるとき,例外を投げること
では,RSpecを満たすように対象クラスを作成します.
# -*- mode:ruby; coding:utf-8 -*- class ClassName def self.method_name(input_1, input_2) raise(ArgumentError, '入力値が不正です.input_1が文字列以外です.') if input_1.class != String raise(ArgumentError, '入力値が不正です.input_2が文字列以外です.') if input_2.class != String return input_1 + input_2 end end
これでRSpecの結果がオールグリーンになりました.
% rspec test_spec.rb -fs [~/ruby/rspec] ClassName method_name 正常系 入力値がともに文字列であるとき,それらを連結した文字列を返却すること 異常系 input_1が文字列以外であるとき,例外を投げること input_2が文字列以外であるとき,例外を投げること Finished in 0.00257 seconds 3 examples, 0 failures
おしまい^^
*1:入力引数それぞれについて,規定の型であるかどうか等