在RSpec测试中抽象出常见的设置步骤

时间:2013-10-28 17:05:45

标签: ruby-on-rails rspec

我正试图在我的RSpec测试中找到创建用户令牌的最佳方法,并尽可能雄辩地编写它们。

以下是我的项目类的一个示例。从下面的规范中,您将看到我正在使用DoorKeeper来保持API端点在除show之外的所有操作上的安全性。

我遇到的问题是如何最好地创建@access_token

这是有效的,通过了所有的例子,但我担心我不遵守DRY原则。如果有很多动作/上下文需要@access_token,那么有什么方法可以将它抽象出来给某个帮助者?

提前致谢

## projects_spec.rb    

require 'spec_helper'

describe "Projects API" do

  describe "#index" do

    FactoryGirl.create(:project)

    context 'with a valid token' do

      before(:each) do 
        user = FactoryGirl.create(:user)
        authentication = FactoryGirl.create(:authentication, user: user)
        application = Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "http://app.com")
        @access_token = Doorkeeper::AccessToken.create!(:application_id => application.id, :resource_owner_id => authentication.identity.id)
      end

      it 'returns a list of projects' do
        get '/api/v1/projects', access_token: @access_token.token
        expect(response.status).to eq(200)

        # check the JSON is as we expect

      end

    end

    context 'without a token' do

      it 'responds with 401' do
        get '/api/v1/projects'
        expect(response.status).to eq(401)
      end

    end

  end

  describe "#create" do

    context 'with a valid token' do

      before(:each) do
        user = FactoryGirl.create(:user)
        authentication = FactoryGirl.create(:authentication, user: user)
        application = Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "http://app.com")
        @access_token = Doorkeeper::AccessToken.create!(:application_id => application.id, :resource_owner_id => authentication.identity.id)
      end

      context 'with required params' do 

        project_params = {
            name: "Win the lottery",
            strapline: "The best feeling in the world"
          }

        it "creates a project and responds with 201" do

          post "/api/v1/projects", :project => project_params, access_token: @access_token.token

          expect(response.status).to eq(201)

          # check the JSON is as we expect

        end

      end

      context 'without required params' do

        project_params = {
            strapline: "Stepney City Farm's pallets, woodchips and compost",
          }

        it "responds with 422 and no record created" do

          post "/api/v1/projects", :project => project_params, access_token: @access_token.token

          expect(response.status).to eq(422)

          json = JSON.parse(response.body)

          expect(json['project']['errors'].length).to eq(1)

        end

      end

    end

    context 'without a token' do

      it 'responds with 401' do
        get '/api/v1/projects'
        expect(response.status).to eq(401)
      end

    end

  end

  describe "#show" do

    it 'returns a projects' do
      project = FactoryGirl.create(:project, name: "A new project")
      get "/api/v1/projects/#{project.id}"
      expect(response.status).to eq(200)
      json = JSON.parse(response.body)
      expect(json['project']['name']).to eq(project.name)
      expect(GroupRun.last.name).to eq(project.name)
      # check the JSON is as we expect
    end

  end

end

3 个答案:

答案 0 :(得分:1)

我有一些技巧可以用来处理这些东西。

第一种是在let上使用普通红宝石方法。这只是我的偏好,我认为它增加了测试的清晰度。请查看此内容以获取更多相关信息:http://robots.thoughtbot.com/lets-not

然后,我有一个auth东西的辅助方法。我正在使用Devise进行身份验证,因此我的实现将与您的实现不同,但是在测试中调用helper方法后,这会为我的应用程序的每个请求设置HTTP_AUTHORIZATION标头。

module Requests
  module AuthenticationHelpers
    def basic_http_auth(user)
      credentials = ActionController::HttpAuthentication::Basic.encode_credentials(user.email,user.password)
      Rack::MockRequest::DEFAULT_ENV['HTTP_AUTHORIZATION'] = credentials
    end
  end
end

然后在实际测试中,我会做这样的事情:

describe "GET /api/v1/messages" do
  it 'returns an array of messages' do
    get '/api/v1/messages'
    basic_http_auth(FactoryGirl.create(:user))

    expect(response).to eq(200)
  end
end

因此,如果您要在许多API测试中使用它,请将其移至支持帮助程序中。并且,如果您在同一个文件中多次调用某些内容,请编写一个方法(或将其置于let调用中)以干燥您的测试。

答案 1 :(得分:1)

根据Matthew的答案,使用门卫,我最终使用了以下内容:

module TokenMacros
  def generate_access_token_for(user = nil)
    user ||= FactoryGirl.create(:user)
    authentication = FactoryGirl.create(:authentication, user: user)
    application = Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "http://app.com")
    access_token = Doorkeeper::AccessToken.create!(:application_id => application.id, :resource_owner_id => authentication.identity.id)
    access_token.token
  end
end

然后允许我打电话给...

let(:user) { FactoryGirl.create(:user) }
let(:token) { generate_access_token_for(user) }

灿烂

答案 2 :(得分:0)

避免重复的最简单方法是在最高级别描述中定义令牌,以便它可用于规范中的所有示例。

此外,为了避免不依赖于它的示例出现任何性能问题,您可以使用let而非before来定义令牌,因为let已被延迟评估

您拥有的是以下内容:

let(:access_token) do
  user = FactoryGirl.create(:user)
  authentication = FactoryGirl.create(:authentication, user: user)
  application = Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "http://app.com")
  Doorkeeper::AccessToken.create!(:application_id => application.id, :resource_owner_id => authentication.identity.id)
  end

您还需要将对此令牌的引用从@access_token更改为access_token,因为您要定义方法而不是实例变量。