使用 RSPEC 进行测试时如何重用 FactorBot 工厂来构建请求属性?

问题描述 投票:0回答:1

我正在使用 rspec 3.12 重构基于 Rails 5.2.4 的应用程序。我专注于尚不存在的请求测试。

在为 Playgrounds 控制器定义 requests 测试时,我希望重新使用有助于验证 Playground 模型的工厂。我在途中遇到了几个错误,现在我在以前使用的登录功能上遇到了困难。以下是第一个请求:

 RSpec.describe "/playgrounds", type: :request do
  
  # Playground. As you add validations to Playground, be sure to
  # adjust the attributes here as well.

  let(:valid_pg) {FactoryBot.create(:playground)}
  let(:invalid_pg) {FactoryBot.create(:playground, status_id: nil)} # Invalid as status is mandatory
  let(:valid_attributes) { valid_pg.attributes.except("id") } # Exclude id as it is generated
  let(:invalid_attributes) { invalid_pg.attributes.except("id") }

# Login as used in the features validation
before do
    get "/users/sign_in"
    test_user = FactoryBot.create(:user, is_admin: true)
    login_as test_user, scope: :user
end

  describe "GET /index" do
    it "1-renders a successful response" do
      valid_attributes["code"].next!

      puts valid_attributes
      puts invalid_attributes
            puts playgrounds_url
      Playground.create! valid_attributes
      get playgrounds_url
      expect(response).to be_successful
    end
  end

  describe "GET /show" do
    it "2-renders a successful response" do
      valid_attributes["code"].next!

      playground = Playground.create! valid_attributes
      get playground_url(playground)
      expect(response).to be_successful
    end
  end

每次测试都会出现以下错误:

 Failure/Error: login_as test_user, scope: :user

 NoMethodError:
   undefined method `login_as' for #<RSpec::ExampleGroups::Playgrounds::GETIndex:0x000001808eda7130>

那么我有2个问题:

  1. 在这里重用工厂听起来是避免 DRY 的好主意。是吗?
  2. 如何在 requests 的上下文中使用 login_as 方法?

谢谢您的帮助!

ruby-on-rails rspec
1个回答
0
投票

你做的集成测试是完全错误的。你真正想要的更像是:

RSpec.describe "Playgrounds", type: :request do
  # This should be handled with a trait
  let(:user) { FactoryBot.create(:user, is_admin: true) }
  let(:playground) { FactoryBot.create(:playground) }

  before do
    # get "/users/sign_in" - don't think you actually need this
    login_as user, scope: :user
  end 

  describe "GET /index" do
    let(:playgrounds) { FactoryBot.create_list(:playground, 3) }
    # Don't do that wonky numbering stuff - you don't need it 
    # and it will just confuse you when the examples run in random order
    it "renders a successful response" do
      get playgrounds_url
      # this is ok as a litmus test but doesn't tell you if the controller
      # is actually providing a meaningful response
      expect(response).to be_successful
    end
    # @todo write a test that it actually responds with the playgrounds
  end

  describe "GET /show" do
    it "renders a successful response" do
      get playground_url(playground)
      # this is ok as a litmus test but doesn't tell you if the controller
      # is actually providing a meaningful response
      expect(response).to be_successful
    end
    # @todo write a test that it actually responds with the playground
  end

  describe "POST /create" do
    context "with valid attributes" do
      # Use per context let instead 
      let(:attributes) { FactoryBot.attributes_for(:playground) }
      it "creates a playground" do
        expect {
          post '/playgrounds', params: {
            playground: attributes
          }
        }.to change(Playground, :count).by(1)
        expect(response).to be_successful
      end
    end 

    context "with invalid attributes" do
      # Use per context let instead 
      let(:attributes) do 
        # I would just define this manually
        {
          foo: ""
        }
      end
      it "creates a playground" do
        expect {
          post '/playgrounds', params: {
            playground: attributes
          }
        }.to change(Playground, :count).by(1)
        expect(response).to be_successful
      end
    end 
  end
end

FactoryBot.create
只能用于在示例运行之前填充数据库。您不应该使用它来尝试创建无效记录,因为这会引发错误。

如果您想在测试创建/更新操作时重用工厂定义,请使用

FactoryBot.attributes_for
获取属性哈希,或使用
FactoryBot#build
获取未保存的模型实例。

在这里重用工厂听起来是避免 DRY 的好主意。是吗?

如果您认同 DRY 方法,那么这不是您想要避免的事情。但是,是的,使用工厂可以用来避免重复,但它们也会降低规范的可读性,因为您必须查看工厂定义才能准确了解传递的内容。

如何在请求上下文中使用login_as方法?

完全取决于您使用的身份验证系统。如果您使用的是 Devise,则只需使用 Warden 提供的 测试助手 即可使身份验证系统短路。这比让每个测试发送额外的 HTTP 请求来登录用户要高效得多。

该路径应该由专门针对身份验证系统的单独测试覆盖。

© www.soinside.com 2019 - 2024. All rights reserved.