2

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

下面是我的Project类的一个示例。从下面的规范中,您将看到我正在使用 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
4

3 回答 3

1

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

首先是使用普通的 ruby​​ 方法let。这只是我的偏好,我认为它增加了测试的清晰度。检查此以获取更多信息:http ://robots.thoughtbot.com/lets-not

然后,我有一个用于身份验证的辅助方法。我正在使用 Devise 进行身份验证,因此我的实现将与您的不同,但这会为在测试中调用辅助方法后对我的应用程序发出的每个请求设置 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调用中)来干燥您的测试。

于 2013-10-30T08:50:26.227 回答
1

基于 Matthew 的回答,与 Doorkeeper 一起,我最终使用了以下内容:

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) }

灿烂

于 2013-11-01T16:36:57.813 回答
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_tokenaccess_token,因为您定义的是方法而不是实例变量。

于 2013-10-30T02:29:49.157 回答