機能テストの際の注意

今まで、機能テストで、1つのテストメソッドの中で何回もリクエストを送るコードを書いていたことがありました。テストケースを1メソッドに割り当てると、どうしてもrake が重くなったり、メソッドが増え過ぎたりとあって、index アクションの処理はtest_index 内で書くとかしてました。

def test_index
  #empty parameters
  get :index
  assert_template "index"
  assert_nil assigns(:hoge)

  get :index, :hoge => 1
  assert_template "index"
  assert_equal "1", assigns(:hoge)
end


ひどいときだと、ループの中にget メソッドがあったりしました。例えば、全てのメソッドの前にフィルタ処理が効いているか確かめるとか。

def test_filter
  methods = @controller.public_methods(false) - ["rescue_action"]
  methods.each do |method|
    get method
    assert_redirected_to :action => "index"
    assert_equal "please login", flash[:error]
  end
end


そもそもこのテストが有益かどうか怪しいです。

1つのテストメソッドで複数のリクエストを送ると危険

例えば、次のテストは見事に落ちます。

def index
  @hoge = params[:hoge] if params[:hoge]
end

def test_index
  get :index, :hoge => 1
  assert_template "index"
  assert_equal "1", assigns(:hoge)

  #empty parameters
  get :index
  assert_template "index"
  assert_nil assigns(:hoge)
end


通常は、リクエストの度に新しいコントローラのインスタンスが生成されるので、2回目のリクエストでは、@hoge の値はnil になります。しかし、テストの場合は、同じメソッド内で複数回のリクエストを行うと、以前の処理結果が残っている状態になります。よって、2回目のリクエストの際には@hoge の中に最初のリクエストの結果が残ってしまっています。
このことを意識しておかないと、誤ったテストを書いてしまう可能性があります。

回避方法

回避方法は2つあります、1つ目は言うまでもなく、メソッドを2つに分ける方法です。そして、もう1つは、以下のように、get の前でsetup メソッドを呼ぶことです。

def test_index
  get :index, :hoge => 1
  assert_template "index"
  assert_equal "1", assigns(:hoge)

  #empty parameters
  setup
  get :index
  assert_template "index"
  assert_nil assigns(:hoge)
end