10

我正在寻找一种干净的方法来使用JBuilder并使用 RSpec 测试 json 输出。JSON 测试的流行方式是实现 as_json 方法,然后在 RSpec 中将接收到的对象与 object.to_json 方法进行比较。但是我使用 JBuilder 的一个重要原因是我不想要 to_json 吐出的所有属性。所以这打破了比较。

目前使用 JBuilder 我必须执行以下操作来测试 RSpec 结果:

1) 创建一个工厂对象:@venue

2) 在我的 RSpec 测试中创建一个包含“预期”JSON 字符串的哈希

@expected => {:id => @venue.id,:name=>@venue.name..........}

2) 将@expected 字符串与从 JSON 调用返回的 results.response.body 进行比较。

这看起来很简单,除了我用 15 个以上的属性渲染对象,并且构建 @expected 哈希字符串很麻烦而且非常脆弱。有一个更好的方法吗?

4

5 回答 5

8

您应该能够使用 RSpec 视图规范测试您的 Jbuilder 视图。您可以在https://www.relishapp.com/rspec/rspec-rails/v/2-13/docs/view-specs/view-spec查看文档。

位于“app/views/api/users/_user.json.jbuilder”的文件的示例规范可能是这样的(spec/views/api/users/_user.json.jbuilder_spec.rb):

require 'spec_helper'

describe 'user rendering' do
  let(:current_user) { User.new(id: 1, email: 'foo@bar.com') }

  before do
    view.stub(:current_user).and_return(current_user)
  end

  it 'does something' do
    render 'api/users/user', user: current_user

    expect(rendered).to match('foo@bar.com')
  end
end
于 2013-03-05T13:35:50.067 回答
6

我不喜欢通过视图测试 JSON API,因为你必须在测试中基本上模仿控制器中已经完成的设置。此外,绕过控制器,您并没有真正测试 API。

但是,在控制器测试中,您会发现响应正文中没有返回任何 JSON。响应正文为空。这是因为 RSpec 在控制器测试中禁用视图渲染。(无论好坏。)

为了对视图呈现的 JSON API 进行 RSpec 控制器测试,您必须render_views在测试顶部添加指令。有关将 RSpec 控制器测试与 Jbuilder 一起使用的更多详细信息,请参阅此博客文章(不是我的)。

另外,请参阅此答案

于 2016-10-30T20:14:23.330 回答
3

我还不能让 RSpec 与视图一起工作,但我正在通过控制器 RSpec 测试来测试我的 JSON API。为了协助这个过程,我使用了api matchers gem。此 gem 可让您构建 RSpec 测试,例如:

it "should have a 200 code" do
  get :list, :format => :json
  response.should be_success
  response.body.should have_json_node( :code ).with( "200" )
  response.body.should have_json_node( :msg ).with( "parameter missing" )
end
于 2012-10-11T04:23:40.893 回答
1

这听起来像是 RSpec 视图规范的一个很好的用例。您是否将 JBuilder 用于视图中控制器的输出?

例如,在 spec/views/venues_spec.rb

require 'spec_helper'
describe "venues/show" do
  it "renders venue json" do
    venue = FactoryGirl.create(:venue)
    assign(:venue, venue])
    render
    expect(view).to render_template(:partial => "_venue")
    venue_hash = JSON.parse(rendered)
    venue_hash['id'].should == @venue.id
  end
end
于 2012-09-11T19:45:24.683 回答
0

它比说ERB有点笨拙,但是您可以使用bindingandeval直接运行 Jbuilder 模板。例如,给定一个引用模型实例的典型Jbuilder模板:app/views/items/_item.json.jbuilderitemItem

json.extract! item, :id, :name, :active, :created_at, :updated_at
json.url item_url(item, format: :json)

假设您有一个返回单个Item对象的端点。在您的请求规范中,您点击了该端点:

get item_url(id: 1), as: :json
expect(response).to be_successful # just to be sure

要获得预期值,您可以按如下方式评估模板:

item = Item.find(1)                          # local variable `item` needed by template
json = JbuilderTemplate.new(JbuilderHandler) # local variable `json`, ditto

template_path = 'app/views/items/_item.json.jbuilder'
binding.eval(File.read(template_path))       # run the template
# or, for better error messages:
#   binding.eval(File.read(template_path), File.basename(template_path))

expected_json = json.target!                 # template result, as a string

然后,您可以将模板输出与原始 HTTP 响应进行比较:

expect(response.body).to eq(expected_json)   # plain string comparison

或者,当然,您可以解析和比较解析后的结果:

actual_value = JSON.parse(response.body)
expected_value = JSON.parse(expected_json)
expect(actual_value).to eq(expected_value)

如果您要经常这样做——或者,例如,如果您希望能够将模板结果与返回的 JSON 数组的各个元素进行比较,您可能需要提取一个方法:

def template_result(template_path, bind)
  json = JbuilderTemplate.new(JbuilderHandler)

  # `bind` is passed in and doesn't include locals declared here,
  # so we need to add `json` explicitly
  bind.local_variable_set(:json, json)

  bind.eval(File.read(template_path), File.basename(template_path))
  JSON.parse(json.target!)
end

然后,您可以执行以下操作:

it 'sorts by name by default' do
  get items_url, as: :json
  expect(response).to be_successful
  parsed_response = JSON.parse(response.body)
  expect(parsed_response.size).to eq(Item.count)

  expected_items = Item.order(:name)
  expected_items.each_with_index do |item, i| # item is used by `binding`
    expected_json = template_result('app/views/items/_item.json.jbuilder', binding)
    expect(parsed_response[i]).to eq(expected_json)
  end
end
于 2021-12-08T21:53:10.073 回答