プロジェクトを管理するサンプルアプリケーションを題材にRSpecの勉強をしています。

サンプルアプリケーションは、プロジェクトを登録することができ、そのプロジェクトは完了済みかどうかのステータス(boolean型のcompleted)を持っています。

プロジェクトの詳細画面には、完了ボタンがあり、完了ボタンを押すと、ステータスが完了済みになります。

この完了ボタンを押すときの、完了処理が失敗したときのフィーチャースペックを書いています。

models/project.rb

class Project < ApplicationRecord
  def complete_pj
    update_attributes(completed: true)
  end
end

controllers/projects_controller.rb

class ProjectsController < ApplicationController
  def complete
    if @project.complete_pj
      redirect_to @project, notice: "Congratulations, this project is complete!"
    else
      redirect_to @project, alert: "Unable to complete project."
    end
  end
end

spec/features/projects_spec.rb

require 'rails_helper'
RSpec.feature "Projects", type: :feature do
  scenario "ユーザはプロジェクトを完了済みにする(しかし失敗する)" do
    # 未完了プロジェクトを持ったユーザを準備する
    user = FactoryBot.create(:user)
    project = FactoryBot.create(:project, owner: user, completed: false)

    # ユーザはログインしている
    sign_in user

    # ユーザがプロジェクト詳細画面を開く。
    visit project_path(project)

    # 完了ボタンをクリックする。
    click_button "Complete" # → ここでProjectController#completeアクションが呼ばれる

    # 完了処理は失敗し、未完了のままとなる。
    expect(project.reload.completed).to eq false
  end
end

完了ボタンを押すことによりProjectController#completeアクションが呼ばれ、その内部で@project.complete_pjが呼ばれます。この「@project.complete_pj」にfalseを返させたいです。

complete_pjメソッドをスタブ化してfalseを返すようにしてやればいいのでは・・・というのは何となくわかるのですが、どうやってコードを書いたらいいかわかりません。

click_button "Complete"の前にallow(project).to receive(:complete_pj).and_return(false)を入れるとパスするのでOKかと思いきや、and_return(true)に変えてもパスするので、そもそもスタブ化がうまくできていないようでした。

宜しくお願いします。