テストの価値の話し。
前提として、銀の弾丸のように様々なシチュエーションに耐える話しではないことは断っておきます。
ソフトウェアテストを知っている人の場合、ランダムな値をテストのインプットとすることは良くないと考える方が多いと思います。一方で、最近ではコミットごとにCIをぐるぐる回すような開発環境も日常化してきました。その、CIをコミットごとにぐるぐる回す環境を活かし、ランダムな値をテストに取り入れ、より効率的なテストができないかとここ数ヶ月思っています。
結論から言えば、同値に区分される入力値に関して、1つのテストケースにおいてCIをまわすごとに異なる値を入力とする機構を仕込むことで、CIをまわせばまわすだけ、1つのテストケースでまわしただけの組み合わせにて入力値の確認ができる、という形です。
ここで、同値に区分されるべき値の中からランダムに要素を選ぶので、試験を失敗するということは実装を誤っているか、テスト(想定する振る舞い)を誤っているかのどちらかといえる可能性があり、それに気づくことができます。
※以下、若干Ruby、RSpecのような記述ですが、単なる例なのでかなり適当なものになってますのであしからず。。。
例えば、以下のようなメソッドがあると過程します。
def login_with(email, pass) do # email, passともに、大文字/小文字の英字 + 数字 とする return true if emailとpassによりログインが成功する end
これに対するテストは以下がよくありますでしょうか。
description '#login_with' do
it 'success' do
let (:email) { 成功する適当な文字列1 }
let (:pass) { 成功する適当な文字列2 }
expect { login_with(email, pass) }.to_be true
end
it 'false' do
let (:email) { 失敗する適当な文字列1 }
let (:pass) { 失敗する適当な文字列2 }
expect { login_with(email, pass) }.to_be false
end
end
この場合、適当な文字列は固定されている文字列を使うことが多く、100回CIをまわしたとしても、1通りの組み合わせしか試験できません。
一方、以下のようなランダムな文字列を生成するメソッドを考えます。
def self.rand_str(length)
a = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
return Array.new(length){a[rand(a.size)]}.join
end
これを使い、成功するテストを以下と仮定します。
description '#login_with' do
it 'success' do
let (:email) { rand_str(10) }
let (:pass) { rand_str(10) }
expect { login_with(email, pass) }.to_be true
end
end
この場合、CIを1回まわす度に、異なる文字列のemail、passの組み合わせを試験できます。
これは、100回まわせば100通りの組み合わせ試験が可能となります。
テストの価値でいうと、100回とも同じ文字列を試験するよりも、100回異なる文字列を試験できるほうが価値のあるものだといえると思います。
ただし、この形の試験を実施できるのは”同値に区分されるテスト対象を試験するとき”となります。
これにより、同値に区分される範囲内をランダムに与えた上で、そのテスト対象の試験結果が意図しないものとなる場合、それは実装が誤っているか、テスト(実装に対する想定)が誤っているかという気づき、改善へとつなげることでしょう。
効率的なテストをもっと充実させ、テストの肥大化を抑制していきたいですね。
1 Comment