Ruby on rails Rails 4 rspec 3控制器测试:会话助手模块在之前不工作(:all),在之前工作(:each)

Ruby on rails Rails 4 rspec 3控制器测试:会话助手模块在之前不工作(:all),在之前工作(:each),ruby-on-rails,rspec,rspec-rails,Ruby On Rails,Rspec,Rspec Rails,我正在使用Rails 4.2.7构建一个玩具聊天应用程序,并且正在使用rspec 3.5为我的控制器编写规范。我的Api::ChatroomsController需要用户登录才能创建聊天室,因此我创建了Api::SessionsHelper模块,从Api::ChatroomsController规范中创建会话 # app/helpers/api/sessions_helper.rb module Api::SessionsHelper def current_user User.fi

我正在使用Rails 4.2.7构建一个玩具聊天应用程序,并且正在使用rspec 3.5为我的控制器编写规范。我的
Api::ChatroomsController
需要用户登录才能创建聊天室,因此我创建了
Api::SessionsHelper
模块,从
Api::ChatroomsController
规范中创建会话

# app/helpers/api/sessions_helper.rb
module Api::SessionsHelper
  def current_user
    User.find_by_session_token(session[:session_token])
  end

  def create_session(user)
    session[:session_token] = user.reset_session_token!
  end

  def destroy_session(user)
    current_user.try(:reset_session_token!)
    session[:session_token] = nil
  end
end


# spec/controllers/api/chatrooms_controller_spec.rb
require 'rails_helper'
include Api::SessionsHelper

RSpec.describe Api::ChatroomsController, type: :controller do
  before(:all) do
    DatabaseCleaner.clean
    User.create!({username: "test_user", password: "asdfasdf"})
  end

  user = User.find_by_username("test_user")

  context "with valid params" do
    done = false

    # doesn't work if using a before(:all) hook
    before(:each) do
      until done do
        create_session(user)
        post :create, chatroom: { name: "chatroom 1" }
        done = true
      end
    end

    let(:chatroom) { Chatroom.find_by({name: "chatroom 1"}) }
    let(:chatroom_member) { ChatroomMember.find_by({user_id: user.id, chatroom_id: chatroom.id}) }

    it "responds with a successful status code" do
      expect(response).to have_http_status(200)
    end

    it "creates a chatroom in the database" do
      expect(chatroom).not_to eq(nil)
    end

    it "adds the chatroom creator to the ChatroomMember table" do
      expect(chatroom_member).not_to eq(nil)
    end
  end

end
我使用带有布尔变量
done
before(:each)
hook来实现
before(:all)
hook的行为,以创建单个会话

如果使用before(:all)钩子,则会出现以下错误:

NoMethodError: undefined method `session' for nil:NilClass`
我在Api::SessionHelper模块的create_session方法中放置了一个调试器来检查
self.class
,在这两种情况下,当我在(:each)之前使用
时,以及当我在(:all)
之前使用
时,类是:

RSpec::ExampleGroups::ApiChatroomsController::WithValidParams
但是,当使用
before(:each)
钩子时,会话是
{}
,而在
before(:all)
钩子中,会话给出了上面的
命名错误


有人知道是什么原因导致此错误吗?

您需要在测试块中包含帮助器:

RSpec.describe Api::ChatroomsController, type: :controller do
  include Api::SessionsHelper
end
通过在
spec/rails\u helper.rb

RSpec.configure do |config|
  # ...
  config.include Api::SessionsHelper, type: :controller
end
这也是您应该放置数据库的位置。您应该使用在每个规范之间进行清理,而不仅仅是在所有规范之前,因为这将导致测试顺序问题和抖动测试

require 'capybara/rspec'

#...

RSpec.configure do |config|

  config.include Api::SessionsHelper, type: :controller
  config.use_transactional_fixtures = false

  config.before(:suite) do
    if config.use_transactional_fixtures?
      raise(<<-MSG)
        Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
        (or set it to false) to prevent uncommitted transactions being used in
        JavaScript-dependent specs.

        During testing, the app-under-test that the browser driver connects to
        uses a different database connection to the database connection used by
        the spec. The app's database connection would not be able to access
        uncommitted transaction data setup over the spec's database connection.
      MSG
    end
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, type: :feature) do
    # :rack_test driver's Rack app under test shares database connection
    # with the specs, so continue to use transaction strategy for speed.
    driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test

    if !driver_shares_db_connection_with_specs
      # Driver is probably for an external browser with an app
      # under test that does *not* share a database connection with the
      # specs, so use truncation strategy.
      DatabaseCleaner.strategy = :truncation
    end
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.append_after(:each) do
    DatabaseCleaner.clean
  end

end
要求“水豚/rspec”
#...
RSpec.configure do | config|
config.include Api::SessionHelper,类型::controller
config.use\u transactional\u fixtures=false
config.before(:suite)do
如果config.use\u事务性\u装置?

raise(还有一些其他问题,比如你应该学习如何使用而不是IVAR,或者使用词法变量,如
user=user。通过用户名(“test\u user”)
查找。你的一般方法也是错误的-你应该使用设置和拆卸阶段(
之前
之后
)把板岩擦干净,然后分别设置每个示例。使用
before\u all
设置某种状态以供文件中的所有示例使用是一种非常有缺陷的方法。感谢您的反馈,我对编写规范相当陌生。如果我的控制器仅呈现json,是否需要
capybara
?我还尝试移动
将Api::SessionHelper
包含在测试块中,以及rails helper中,当我试图在(:all)
hook之前的
中使用
创建会话时,这两种情况仍然会给我相同的错误。“如果我的控制器只呈现json,是否需要capybara?”-不,不是。Capybara是一个web爬虫程序,它解析HTML并模拟用户在应用程序中点击。如果您正在编写一个仅使用API的应用程序,则需要使用请求规范,并确保来自API的响应与API的文档相匹配。您不应该在所有
钩子之前在
钩子中使用它!您需要进行设置每个示例的状态。请在
之前使用
,并确保在每个示例之后重置会话以注销用户。