【问题标题】:How to use RSpec with JBuilder?如何将 RSpec 与 JBuilder 一起使用?
【发布时间】:2012-04-14 08:04:30
【问题描述】:

我正在寻找一种干净的方式来使用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 哈希字符串既麻烦又非常脆弱。有没有更好的方法来做到这一点?

【问题讨论】:

  • 我一直在这样做。我想不出更好的方法来做到这一点。我构造了一个哈希并在@expected 上应用.to_json。我做的一件事是加快速度并使其不那么脆弱。也许以准确性为代价?是用收集和映射构建我的哈希。我猜想使用任何 JSON 字符串构建库构建自定义模板的具体性质要求您编写一个与您所做工作的细节相匹配的测试。:/
  • @beeudoublez 你能使用 RSpec 来测试 jbuilder 视图吗?我无法让 rspec 将对象传递给视图,以便 jbuilder 处理程序构造 JSON。你有工作视图*_spec.rb文件的例子吗?

标签: ruby-on-rails unit-testing rspec jbuilder


【解决方案1】:

您应该能够使用 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

【讨论】:

  • 弃用警告:不推荐使用来自 rspec-mocks 的旧 :should 语法的 stub 而不显式启用该语法。改用新的:expect 语法或显式启用:should
【解决方案2】:

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

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

为了对您的视图呈现的 JSON API 进行 RSpec 控制器测试,您必须在测试顶部添加 render_views 指令。请参阅this blog post(不是我的),了解有关使用 Jbuilder 使用 RSpec 控制器测试的更多详细信息。

另外,请参阅this answer

【讨论】:

    【解决方案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
    

    【讨论】:

      【解决方案4】:

      这听起来像是 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
      

      【讨论】:

        【解决方案5】:

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

        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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-04-24
          • 2014-11-21
          • 1970-01-01
          • 1970-01-01
          • 2012-07-27
          • 2012-09-10
          • 1970-01-01
          相关资源
          最近更新 更多