RSpecを始めたばかりの人にとって、letやcontextは最初は少し難しく思えるかもしれません。
しかし、letやcontextを使いこなすことでテストの見通しを格段に良くすることができます。
このエントリでは、RSpecのletとcontextの使ってテストの見通しを良くする方法を解説します。
今回テストするのは次のように与えられた引数を加算するaddメソッドです。
1点注意しなければならないのは、引数a, bのどちらかが整数値でなければnilを返す点です。
class Calculator
def add(a, b)
return unless a.is_a?(Integer) && b.is_a?(Integer)
a + b
end
end 実行結果も見ておきましょう。実行結果は次のようになります。
calc = Calculator.new
puts "a: 1, b: 2 => #{calc.add(1, 2)}" # a: 1, b: 2 => 3
puts "a: nil, b: 2 => #{calc.add(nil, 2)}" # a: nil, b: 2 =>
puts "a: 1, b: nil => #{calc.add(1, nil)}" # a: 1, b: nil => addメソッドのテストを、最初はletやcontextを使わないで書いてみます。おそらく次のようになるでしょう。
RSpec.describe "Calculator" do
describe "#add" do
it "returns the sum" do
a = 1
b = 2
calc = Calculator.new
expect(calc.add(a, b)).to eq(a + b)
end
end
end しかし、これではaやbが整数値でない場合のテストが足りません。そこでcontextの出番です。
まず、contextによってテストのコンテキストを明示してみます。
aとbが整数値であるときを表すためにcontext "when both a and b are integer"を追加します。
RSpec.describe "Calculator" do
describe "#add" do
context "when both a and b are integer" do
it "returns the sum" do
a = 1
b = 2
calc = Calculator.new
expect(calc.add(a, b)).to eq(a + b)
end
end
end
end これによって「aとbの両方が整数値のとき」が明確になりました。
同じようにして「aが整数値でないとき」と「bが整数値でないとき」のコンテキストを追加します。
RSpec.describe "Calculator" do
describe "#add" do
context "when both a and b are integer" do
...
end
context "when a is not an integer" do
it "returns nil" do
a = nil
b = 2
calc = Calculator.new
expect(calc.add(a, b)).to ne_nil
end
end
context "when b is not an integer" do
it "returns nil" do
a = 1
b = nil
calc = Calculator.new
expect(calc.add(a, b)).to be_nil
end
end
end
end contextとitを読むだけで、どんなときに何が返ってくるかがわかりやすいテストになりました。
しかし、contextごとに変わる引数がどうなっているかは、itの中身を見なければわかりません。
そこでletを使ってさらに見通しを良くします。
contextを使って「aとbが整数値のとき」や「aが整数値でないとき」を表すことができました。
しかし、本当にaやbがそうなっているか、実際に何が入っているかはitの中身を見なければわかりませんでした。
そこでletを使ってaとbを定義してあげることで見通しを良くします。
RSpec.describe "Calculator" do
describe "#add" do
context "when both a and b are integer" do
let(:a) { 1 }
let(:b) { 2 }
it "returns the sum" do
...
end
end
context "when a is not an integer" do
let(:a) { nil }
let(:b) { 2 }
it "returns nil" do
...
end
end
context "when b is not an integer" do
let(:a) { 1 }
let(:b) { nil }
it "returns nil" do
...
end
end
end
end aとbがcontextの近くに移動したことで、コンテキストごとの変数の定義が明確になりました。
さらに、上位のブロックで共通のletを定義しておくことでもっとシンプルにできます。
下記が完成の形です。
RSpec.describe "Calculator" do
describe "#add" do
let(:a) { 1 }
let(:b) { 2 }
context "when both a and b are integer" do
it "returns the sum" do
calc = Calculator.new
expect(calc.add(a, b)).to eq(a + b)
end
end
context "when a is not an integer" do
let(:a) { nil }
it "returns nil" do
calc = Calculator.new
expect(calc.add(a, b)).to ne_nil
end
end
context "when b is not an integer" do
let(:b) { nil }
it "returns nil" do
calc = Calculator.new
expect(calc.add(a, b)).to be_nil
end
end
end
end ここではletを単純な変数の定義にしか使っていませんが、letにはブロックの評価をメモ化するという有用な役割があります。
letのメリットについて詳しくは下記の記事を参照してみてください。
以上です。
このエントリでは、RSpecのletとcontextの使ってテストの見通しを良くする方法を解説しました。
コメントを送る
コメントはブログオーナーのみ閲覧できます