英文:
Set Session in RSpec with Rails 7 API
问题
以下是您要翻译的代码部分:
class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super
    if @user
      # 如果用户在会话中有群组,我们为该用户创建一个群组成员
      Membership.create(user: @user, group: session[:group]) if session[:group]
      # 移除会话中的群组信息
      reset_session
    end
  end
end
context "当用户通过邀请注册时" do
  let(:group) {create :group}
  before do
    # 我们需要创建一个图标,以便在创建用户时将其附加到用户
    create :icon
    
    post user_registration_path,
    params: { "user": { "username": "test", "email": "myemail@email.com", "password": "mypassword" }},
    session: { "group": group.id }
  end
  it "保存了一个带有群组ID的会话" do
    expect(session[:group]).to eq group.id
  end
end
我希望这些代码部分对您有所帮助。如果您需要更多帮助,请随时告诉我。
英文:
I am trying to create a logic where a user gets an invitation from a group. If they signup via that invitation, the group id will be stored in the session and when they signup, they will be automatically added to the group as a member.
I am using Devise for authentication. So I customized it like this:
class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super
    if @user
      # We create a membership to the group if the user had it in the session (it means that it was invited)
      Membership.create(user: @user, group: session[:group]) if session[:group]
      # We remove the existing session
      reset_session
    end
  end
end
Now, I want to test this behaviour.
I would like to test 2 scenarios:
- User doesn't have the session and signs up just like that
 - User has the session and signs up -> a group membership is generated
 
The first one is fine but the second one is the one I am struggling at:
context "when user signs up via invitation" do
 let(:group) {create :group}
 before do
  # We need to create an icon so that we can attach it to the use when created
  create :icon
  
  post user_registration_path,
  params: { "user": { "username": "test", "email": "myemail@email.com", "password": "mypassword" }},
  session: { "group": group.id }
 end
 it "has a session saved with group id" do
  expect(session[:group]).to eq group.id
 end
end
I found this way here, but it throws the following error:
ArgumentError:
       unknown keyword: :session
If I try making the call like this instead:
params: { user: { "username": "test", "email": "myemail@email.com", "password": "mypassword" }, session: { "group": group.id}}
It still throws an error:
NoMethodError:
       undefined method `enabled?' for {}:Hash
     
               return unless session.enabled?
I also tried setting it like this:
request.session[:group] = group.id
After I make the post call (with only the params). It does pass the expectation but I cannot fetch it from the controller.
Also, just setting it like this:
session[:group] = group.id
Throws the following error:
NoMethodError:
       undefined method `session' for nil:NilClass
     
             @request.session
Finally, if I try to mock it inside the before {} block:
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return( group: group.id )
It gives me the following error:
NoMethodError:
       undefined method `enabled?' for {:group=>1}:Hash
     
               return unless session.enabled?
Any ideas on how can I tackle this?
PD. Using Rails 7 API and ruby 3.1.2
and also in my application.rb I added the following to be able to work with sessions in the app (and when I test manually it does work)
# Configure session storage
config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options
Thanks!
答案1
得分: 1
I also struggled with this until I followed this approach and included a "session double" support, like this example:
Support:
https://github.com/DFE-Digital/schools-experience/blob/master/spec/support/session_double.rb
Usage:
Your Example
# your test...
RSpec.describe '...', type: :request do
  describe '...' do
    include_context 'session double'
    let(:group) { create :group }
    let(:session_hash) { {} }
    before do
      session_hash[:group] = group.id
    end
    it 'has a session saved with group id' do
      post user_registration_path, params: { "user": { "username": "test", "email": "myemail@email.com", "password": "mypassword" }}
      expect(session[:group]).to eq group.id
    end
  end
end
Add Session Support:
# spec/support/session_double.rb
shared_context 'session double' do
  let(:session_hash) { {} }
  before do
    session_double = instance_double(ActionDispatch::Request::Session, enabled?: true, loaded?: false)
    allow(session_double).to receive(:[]) do |key|
      session_hash[key]
    end
    allow(session_double).to receive(:[]=) do |key, value|
      session_hash[key] = value
    end
    allow(session_double).to receive(:delete) do |key|
      session_hash.delete(key)
    end
    allow(session_double).to receive(:clear) do |_key|
      session_hash.clear
    end
    allow(session_double).to receive(:fetch) do |key|
      session_hash.fetch(key)
    end
    allow(session_double).to receive(:key?) do |key|
      session_hash.key?(key)
    end
    allow_any_instance_of(ActionDispatch::Request)
      .to receive(:session).and_return(session_double)
  end
end
英文:
I also struggled with this until I followed this approach and included a "session double" support, like this example:
Support:
https://github.com/DFE-Digital/schools-experience/blob/master/spec/support/session_double.rb
Usage:
Your Example
# your test...
RSpec.describe '...', type: :request do
  describe '...' do
    include_context 'session double'
    let(:group) {create :group}
    let(:session_hash) { {} }
    before do
      session_hash[:group] = group.id
    end
    it 'has a session saved with group id' do
      post user_registration_path, params: { "user": { "username": "test", "email": "myemail@email.com", "password": "mypassword" }}
      expect(session[:group]).to eq group.id
    end
  end
end
Add Session Support:
# spec/support/session_double.rb
shared_context 'session double' do
  let(:session_hash) { {} }
  before do
    session_double = instance_double(ActionDispatch::Request::Session, enabled?: true, loaded?: false)
    allow(session_double).to receive(:[]) do |key|
      session_hash[key]
    end
    allow(session_double).to receive(:[]=) do |key, value|
      session_hash[key] = value
    end
    allow(session_double).to receive(:delete) do |key|
      session_hash.delete(key)
    end
    allow(session_double).to receive(:clear) do |_key|
      session_hash.clear
    end
    allow(session_double).to receive(:fetch) do |key|
      session_hash.fetch(key)
    end
    allow(session_double).to receive(:key?) do |key|
      session_hash.key?(key)
    end
    allow_any_instance_of(ActionDispatch::Request)
      .to receive(:session).and_return(session_double)
  end
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论