ตรวจสอบความถูกต้องในข้อกำหนดคำขอ


84

เมื่อเขียนข้อมูลจำเพาะของคำขอคุณจะกำหนดวิธีการเซสชันและ / หรือตัวควบคุมต้นขั้วได้อย่างไร? ฉันพยายามที่จะตัดการรับรองความถูกต้องในการทดสอบการรวม - rspec / request

นี่คือตัวอย่างของการทดสอบ

require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/authentication_helpers'


describe "Messages" do
  include AuthenticationHelpers

  describe "GET admin/messages" do
    before(:each) do
      @current_user = Factory :super_admin
      login(@current_user)
    end

    it "displays received messages" do
      sender = Factory :jonas
      direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
      direct_message.save
      get admin_messages_path
      response.body.should include(direct_message.subject) 
    end
  end
end

ผู้ช่วย:

module AuthenticationHelpers
  def login(user)
    session[:user_id] = user.id # session is nil
    #controller.stub!(:current_user).and_return(user) # controller is nil
  end
end

และ ApplicationController ที่จัดการการพิสูจน์ตัวตน:

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user
  helper_method :logged_in?

  protected

  def current_user  
    @current_user ||= User.find(session[:user_id]) if session[:user_id]  
  end

  def logged_in?
    !current_user.nil?
  end
end

เหตุใดจึงไม่สามารถเข้าถึงทรัพยากรเหล่านี้ได้

1) Messages GET admin/messages displays received messages
     Failure/Error: login(@current_user)
     NoMethodError:
       undefined method `session' for nil:NilClass
     # ./spec/requests/authentication_helpers.rb:3:in `login'
     # ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'

คำตอบ:


101

ข้อมูลจำเพาะของคำขอคือกระดาษห่อหุ้มบาง ๆActionDispatch::IntegrationTestซึ่งใช้ไม่ได้เหมือนกับข้อกำหนดของคอนโทรลเลอร์ (ซึ่งห่อหุ้มActionController::TestCase) แม้ว่าจะมีวิธีเซสชันให้ใช้งาน แต่ฉันก็ไม่คิดว่ามันได้รับการสนับสนุน (นั่นอาจเป็นเพราะโมดูลที่รวมไว้สำหรับยูทิลิตี้อื่น ๆ ก็มีวิธีนั้นด้วย)

ฉันขอแนะนำให้เข้าสู่ระบบโดยโพสต์การดำเนินการใด ๆ ที่คุณใช้ในการตรวจสอบสิทธิ์ผู้ใช้ หากคุณสร้างรหัสผ่าน 'รหัสผ่าน' (ตัวอย่าง) สำหรับโรงงานผู้ใช้ทั้งหมดคุณสามารถทำสิ่งนี้ได้:

เข้าสู่ระบบ def (ผู้ใช้)
  โพสต์ login_path,: login => user.login,: password => 'password'
จบ

1
ขอบคุณเดวิด มันใช้งานได้ดี แต่ดูเหมือนว่าจะเกินความจำเป็นเล็กน้อยในการร้องขอทั้งหมดนั้น?
Jonas Nielsen

19
ถ้าฉันคิดว่ามันเกินความจริงฉันคงไม่แนะนำ :)
David Chelimsky

6
นอกจากนี้ยังเป็นวิธีที่ง่ายที่สุดที่จะทำได้อย่างน่าเชื่อถือ ActionDispatch::IntegrationTestได้รับการออกแบบมาเพื่อจำลองผู้ใช้อย่างน้อยหนึ่งคนที่โต้ตอบผ่านเบราว์เซอร์โดยไม่ต้องใช้เบราว์เซอร์จริง อาจมีผู้ใช้มากกว่าหนึ่งคน (เช่นเซสชัน) และคอนโทรลเลอร์มากกว่าหนึ่งตัวในตัวอย่างเดียวและอ็อบเจ็กต์เซสชัน / คอนโทรลเลอร์เป็นสิ่งที่ใช้ในคำขอสุดท้าย คุณไม่มีสิทธิ์เข้าถึงก่อนที่จะร้องขอ
David Chelimsky

17
ฉันต้องใช้page.driver.postกับ Capybara
Ian Yang

@IanYang page.driver.postอาจจะเป็นปฏิปักษ์ตาม Jonas Nicklas ในCapybara และ API การทดสอบ )
Epigene

61

หมายเหตุสำหรับผู้ใช้ Devise ...

BTW คำตอบ @ David Chelimsky อาจจำเป็นต้องมีการปรับเปลี่ยนเล็ก ๆ น้อย ๆ ถ้าคุณกำลังใช้ประดิษฐ์ สิ่งที่ฉันกำลังทำในการทดสอบการรวม / การร้องขอ (ขอบคุณโพสต์ StackOverflow นี้ ):

# file: spec/requests_helper.rb

# Rails 6
def login(user)
  post user_session_path, params: {
    user: {
      email: user.email, password: user.password
    }
  }
  follow_redirect!
end

# Rails 5 or older
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

2
เมื่อฉันใช้ 'login user1' ในข้อมูลจำเพาะโมเดล rspec ฉันได้รับตัวแปรหรือวิธีการ 'user_session_path' ที่ไม่ได้กำหนดสำหรับ # <RSpec :: Core:
jpw

1
ถือว่าคุณมีdevise_for :usersอยู่ในconfig/routes.rbไฟล์ หากคุณระบุสิ่งที่แตกต่างออกไปคุณจะต้องปรับแต่งโค้ดของคุณตามนั้น
Fearless_fool

สิ่งนี้ใช้ได้ผลสำหรับฉันฉันต้องแก้ไขเล็กน้อย ฉันเปลี่ยน'user[email]' => user.emailเป็น'user[username]' => user.usernameเนื่องจากแอปของฉันใช้ชื่อผู้ใช้เป็นข้อมูลเข้าสู่ระบบแทนอีเมล
webdevguy

3

FWIW ในการย้ายการทดสอบ :: การทดสอบหน่วยไปยัง RSpec ฉันต้องการเข้าสู่ระบบด้วยเซสชัน (ประดิษฐ์) หลาย ๆ เซสชันในข้อกำหนดคำขอของฉัน ต้องใช้เวลาในการขุด แต่ทำให้สิ่งนี้ได้ผลสำหรับฉัน การใช้ Rails 3.2.13 และ RSpec 2.13.0

# file: spec/support/devise.rb
module RequestHelpers
  def login(user)
    ActionController::IntegrationTest.new(self).open_session do |sess|
      u = users(user)

      sess.post '/users/sign_in', {
        user: {
          email: u.email,
          password: 'password'
        }
      }

      sess.flash[:alert].should be_nil
      sess.flash[:notice].should == 'Signed in successfully.'
      sess.response.code.should == '302'
    end
  end
end

include RequestHelpers

และ...

# spec/request/user_flows.rb
require 'spec_helper'

describe 'User flows' do
  fixtures :users

  it 'lets a user do stuff to another user' do
    karl = login :karl
    karl.get '/users'
    karl.response.code.should eq '200'

    karl.xhr :put, "/users/#{users(:bob).id}", id: users(:bob).id,
      "#{users(:bob).id}-is-funny" => 'true'

    karl.response.code.should eq '200'
    User.find(users(:bob).id).should be_funny

    bob = login :bob
    expect { bob.get '/users' }.to_not raise_exception

    bob.response.code.should eq '200'
  end
end

แก้ไข : แก้ไขการพิมพ์ผิด


-1

คุณสามารถหยุดเซสชันได้อย่างง่ายดายเช่นกัน

controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)

ตัวดำเนินการพิเศษทับทิมทั้งหมดเป็นวิธีการ การโทร1+1ก็เหมือนกับ1.+(1)ซึ่งหมายความว่า+เป็นเพียงวิธีการ ในทำนองเดียวกันsession[:user_id]ก็เหมือนกับวิธีการเรียก[]บนsessionเช่นเดียวกับsession.[](:user_id)


นี่ดูเหมือนเป็นวิธีแก้ปัญหาที่สมเหตุสมผล
superluminary

2
สิ่งนี้ใช้ไม่ได้ในข้อกำหนดคำขอ แต่เฉพาะในข้อมูลจำเพาะของคอนโทรลเลอร์เท่านั้น
Machisuji

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.