如何使用Sinatra + rspec测试我的JSON API

时间:2013-02-21 19:42:58

标签: api rspec sinatra

我有一个接受JSON的帖子方法:

post '/channel/create' do
  content_type :json

  @data = JSON.parse(env['rack.input'].gets)

  if @data.nil? or !@data.has_key?('api_key')
    status 400
    body({ :error => "JSON corrupt" }.to_json)
  else
    status 200
    body({ :error => "Channel created" }.to_json)
  end

作为rspec的新手,我很困惑,试图弄清楚如何使用可接受的JSON有效负载编写针对该POST的测试。我最接近的是这是非常不准确但我似乎没有问谷歌上帝正确的问题,以帮助我在这里。

  it "accepts create channel" do
    h = {'Content-Type' => 'application/json'}
    body = { :key => "abcdef" }.to_json
    post '/channel/create', body, h
    last_response.should be_ok
  end

在Sinatra中测试API的任何最佳实践指南也将非常受欢迎。

3 个答案:

答案 0 :(得分:11)

你使用过的代码很好,虽然我的结构略有不同,因为我不喜欢像你通常看到的那样使用it块,我认为它鼓励测试不止一个方面一次一个系统:

let(:body) { { :key => "abcdef" }.to_json }
before do
  post '/channel/create', body, {'CONTENT_TYPE' => 'application/json'}
end
subject { last_response }
it { should be_ok }

我使用过let,因为它比before块中的实例变量更好(不赞成你这样做)。 post位于before区块中,因为它不是规范的一部分,而是在您选择之前发生的副作用。 subject是响应,它使it成为一个简单的呼叫。

因为需要检查响应是否正常所以我经常把它放在shared example

shared_examples_for "Any route" do
  subject { last_response }
  it { should be_ok }
end

然后将其称为:

describe "Creating a new channel" do
  let(:body) { { :key => "abcdef" }.to_json }
  before do
    post '/channel/create', body, {'CONTENT_TYPE' => 'application/json'}
  end
  it_should_behave_like "Any route"
  # now spec some other, more complicated stuff…
  subject { JSON.parse(last_response.body) }
  it { should == "" }

因为内容类型经常变化,所以我把它放在帮助器中:

  module Helpers

    def env( *methods )
      methods.each_with_object({}) do |meth, obj|
        obj.merge! __send__(meth)
      end
    end

    def accepts_html
      {"HTTP_ACCEPT" => "text/html" }
    end

    def accepts_json 
      {"HTTP_ACCEPT" => "application/json" }
    end

    def via_xhr      
      {"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"}
    end

通过RSpec配置将其添加到需要的地方很容易:

RSpec.configure do |config|
  config.include Helpers, :type => :request

然后:

describe "Creating a new channel", :type => :request do
  let(:body) { { :key => "abcdef" }.to_json }
  before do
    post '/channel/create', body, env(:accepts_json)
  end

说了这么多,就个人而言,我不会发布使用JSON。 HTTP POST易于处理,每个表单和JavaScript库都可以轻松完成。通过各种方式响应JSON,但不发布JSON,HTTP更容易。


编辑:在写出Helpers位之后,我意识到it would be more helpful as a gem

答案 1 :(得分:0)

在此提交中,似乎将post :update, '{"some": "json"}'的功能添加到rspec使用的内部ActionPack test_case.rb中: https://github.com/rails/rails/commit/5b9708840f4cc1d5414c64be43c5fc6b51d4ecbf

由于您正在使用Sinatra,我不确定获得这些更改的最佳方法 - 您可以直接升级ActionPack,也可以从上面的提交中进行修补。

答案 2 :(得分:0)

如果您想将last_response视为JSON,可以尝试rack-test-json which makes this trivial

expect(last_response).to be_json
expect(last_response.as_json['key']).to be == 'value'