kei-p3’s blog

kei-pによる技術共有と思考整理

rspecのdescribe, context, subject, letなどの書き分け

Everyday Railsを読んでみて、rspecの利便さに改めて気付かされました。 特にAPI周りの実装をすることの多かった自分としては、capybaraによるfeatureテストなんかはこんなことがこんなに簡単にできるのかと驚きました。

しかし、自分としては知りたかった、describecontextletの書き分けといった細かな部分についてはあまり深く触れていなかったので、
自分のいままでの経験上で「こんな感じでやってます」というのを、まとめついでに紹介したいと思います。

テスト名の宣言 - describe, context, it

describecontextについては、どちらも同じ挙動をするので、テストとしてはどっちで書いても機能するのですが、
言葉の意味合いから下記のように使い分けています。

itについては、エラーがでたときに内容を理解できればいいかなということで、複雑な内容でない限りは記述してないことのほうが多いです。

区分名 使い方
describe テストグループ ( クラス名, メソッド名, )
context テスト条件 ( 変数の違いや前提条件の違い )
it テスト結果
describe API::UserAuth do
    describe "POST /api/session" do
        context 'with valid password and email' do
            it do
                ....
            end
        end
         
        context 'with invalid password' do
            it do
                ...
            end
        end
        
        context 'when user has already signed in ' do
            it do
                ...
            end
        end
    end
end

変数宣言について - subject / let / インスタンス変数

どちらも変数を扱う記述ですが、subjectの使い道としては、そのテストする内容自身を記述します。
一方、letはそれ以外のテスト内で扱う変数の記述します。
インスタンス変数については、原則使わないほうが良いと思われる。というのも、テストのために、before文の中でインスタンス変数を宣言する形となってしまい、記述が見辛くなってしまう。 書き方を工夫すれば、letで代用できるので、こちらを扱うほうが適切だと思われる。

なお、subjectについては名前付きで宣言することもでき、sign_inのように返り値のない、副作用の生じるメソッドなどで、その対象を名前付きのsubjectをとして宣言して、テスト対象であることを明示するといいかも。

区分名 使い方
subject テスト対象 = expectの引数になるもの
let expectの引数になり得ないもの
@hoge 使わない
describe User do
    describe '#sign_in' do
        subject { user.sign_in(email, password) }
        subject(:user) { User.new(email: "hoge@example.com", password: "password") }
        
        context "with valid password and email" do
            let(:email) { "hoge@example.com" }
            let(:last_name) { "password" }
            
            it { expect { subject }.to change(UserSession, :count).by(1) }
            it { expect { subject }.to change(user, :session) }
        end
    end

beforeについて

beforeについては、テストを行う上での前提条件となるような内容を記述する。
主に、letでの変数宣言だけでは対処できないような処理はこのbeforeに書くようにしてます。

     context 'when user has already signed in' do
            before { user.sign_in(email: "hoge@example.com", password: "password") }
        
            it do
                ...
            end
        end

最後に

綺麗に書けたテストは見やすく、メンテもしやすいです。
テストだからと軽視せずに、テストも綺麗に書けるようになるといいですね!