Twitter以上ブログ以下

ただの読み物

rails で cookies を使ったテストをrequest, feature spec ではない書き方でテストするための方法が知りたい

この記事でわかること

request spec や feature spec ではない、 cookies だけをどうにかして取り出してテストを書きたいフレンズが、テストを書けるといいなぁって思えるようになる。 ただし、厳密には違う。違うんだ……どうしたらいんだ……教えてくれ…………(ほんとう?

背景

例えば以下のようなコードでテストを書きたかった

# app/models/state.rb # めっちゃだめなクラス名だな……
class State
  class CookiesAdapter
    def initialize(cookies)
      @cookies = cookies
    end

    def transition!(key)
      @cookies['state_transition'] = { value: key }
    end

    def in?(key)
      @cookies['state_transition'] = 
    end
  end

  def initialize(adapter)
    @adapter = adapter
  end
  
  def transition!(key)
    @adapter.transition!(key)
  end

  def in?(key)
    @adapter.in?(key)
  end
end

class HogeController
  def show
    @state = State.new(State::CookiesAdapter.new(cookies))
  end
  # ... 
end

State インスタンスの振る舞いは、実際には hogeAdapter の同名のメソッドを呼ぶことで、上の実装では CookiesAdapter しかないが、まぁRedisなりなんなりのAdapterがあればいい感じに振る舞いを分離できて幸せそう。 AdapterPattern のようなナニだ。

で、Cookiesを使いたい時に、 Rails では ActionDispatch::Cookies に実装*1があり、他にもあるが一番わかり易いのが ActionDispatch::Request#cookies_jar*2Cookies::CookiesJar.build 経由で cookies_jar を作って、その alias として cookies*3でのアクセスが実現できている。

困ること

  1. Cookies を直接インスタンス化するために必要なパラメーターがわからん(調べればいいんですが)
  2. request spec として、リクエストが透けた形で↑のモデルのテストを書きたくない
  3. feature spec として以下略
  4. ActionControllerインスタンス化でもいいけど、なんかもう少し上手いやつないかなぁ

ということでテストの書き方二種

二種? というのは、rails のバージョンで異なるため。古いバージョンとかは知らない。4.2は古いっていうのは、はいそうですね。

rails 4.2

RSpec.describe State::CookiesAdapter do
  let(:cookies) { ActionDispatch::Request.new({}).cookies }

え、なにそれ辛い。辛いというか、 とくに new({}) あたりが、「本当にそうなのか」がワカラナイ。 一応、 Rails.application.env_config でよいっぽいというStackOverflowの気持ちを見ることができるが、ほんと〜?となる。

unit testing - Setup cookie.signed in Rails 5 controller integration tests - Stack Overflow

rails 5

RSpec.describe State::CookiesAdapter do
  let(:cookies) { ActionDispatch::Request.empty.cookie_jar }

https://github.com/rails/rails/blob/master/actionpack/test/dispatch/cookies_test.rb#L12 rails が 自らのテスト↑で @requestRequest.empty で作成してそこから cookie_jar を取ってる。( 厳密には cookies_jar )

なんか結局イマイチだけど、まぁ rails がそうやってるなら、まぁいいんじゃないかな? となる。

おわりに

  1. rails5 を使おう
  2. そうでないなら ActionDispatch::Request.new({}).cookie_jar で一応取れる。ほんと〜?