【问题标题】:How to test JSON result from Ruby on Rails functional tests?如何从 Ruby on Rails 功能测试中测试 JSON 结果?
【发布时间】:2008-12-03 10:07:30
【问题描述】:

如何断言我的 Ajax 请求并测试来自 Ruby on Rails 功能测试的 JSON 输出?

【问题讨论】:

    标签: ruby-on-rails json tdd


    【解决方案1】:

    在 Rails 中 >= 5

    使用ActionDispatch::TestResponse#parsed_body

    例子:

    user = @response.parsed_body
    assert_equal "Mike", user['name']
    

    在高达 4.x 的 Rails 中

    使用JSON.parse,它将字符串作为输入并返回 JSON 表示的 Ruby 哈希。

    例子:

    user = JSON.parse(@response.body)
    assert_equal "Mike", user['name']
    

    【讨论】:

    • 通过添加 OpenStruct,您可以减少输入:user = JSON.parse(@response.body, object_class: OpenStruct)assert_equal "Mike", user.name
    【解决方案2】:

    Rails 内置了 JSON 支持:

    def json_response
        ActiveSupport::JSON.decode @response.body
    end
    

    不需要插件

    然后你可以这样做:

    assert_equal "Mike", json_response['name']
    

    【讨论】:

    • 为了性能,您可能需要类似:@json_response ||= ActiveSupport::JSON.decode @response.body
    • Alex:那不会缓存第一个测试的响应并在所有后续 JSON 测试中返回吗?
    • 这将缓存一个测试函数中所有请求的响应,这可能不是一个理想的效果。澄清一下,它不会缓存 ACROSS 测试。
    【解决方案3】:

    如果您使用的是 RSpec,json_spec 值得一看

    https://github.com/collectiveidea/json_spec

    【讨论】:

      【解决方案4】:

      对于简短的 JSON 响应,您可以简单地将 JSON 字符串与@response.body 匹配。这样可以避免不得不依赖另一个 gem。

      assert_equal '{"total_votes":1}', @response.body
      

      【讨论】:

        【解决方案5】:

        在较新版本的 Rails 中,您可以利用 parsed_body 在您的测试中无需任何工作即可访问它。

        在响应上调用 parsed_body 会根据最后一个响应的 MIME 类型解析响应正文。

        开箱即用,仅支持 :json。但是对于您注册的任何自定义 MIME 类型,您可以添加自己的编码器...

        https://api.rubyonrails.org/v5.2.1/classes/ActionDispatch/IntegrationTest.html

        【讨论】:

          【解决方案6】:

          没有一个答案提供了一种很好的可维护方式来验证 JSON 响应。我觉得这个是最好的:

          https://github.com/ruby-json-schema/json-schema

          它为标准json schema提供了一个很好的实现

          您可以编写如下架构:

          schema = {
              "type"=>"object",
              "required" => ["a"],
              "properties" => {
                  "a" => {
                      "type" => "integer",
                      "default" => 42
                  },
                  "b" => {
                      "type" => "object",
                      "properties" => {
                          "x" => {
                              "type" => "integer"
                          }
                      }
                  }
              }
          }
          

          并像这样使用它:JSON::Validator.validate(schema, { "a" => 5 })

          根据我的 android 客户端实现验证它的最佳方式。

          【讨论】:

            【解决方案7】:

            其实你可以隐式使用 JSON 模块:

            assert_equal assigns(:user).to_json, @response.body
            

            【讨论】:

              【解决方案8】:

              如上所述,您使用 JSON.parse 来测试 JSON,但执行该断言的位置取决于您呈现 JSON 的方式。

              如果您在控制器中生成 JSON,则在控制器功能测试中解析 JSON(如其他答案所示)。如果您使用 Jbuilderrabl 或其他采用这种方法的 gem 来渲染 JSON,那么 parse the JSON in the view unit tests 不是控制器功能测试。单元测试通常执行起来更快,更容易编写 - 例如,您可以在内存中构建模型,而不是在数据库中创建它们。

              【讨论】:

                【解决方案9】:

                您可以将AssertJson gem 用于一个不错的 DSL,它允许您检查 JSON 响应中应该存在的键和值。

                将 gem 添加到您的 Gemfile:

                group :test do
                  gem 'assert_json'
                end
                

                这是一个简单的示例,您的功能/控制器测试可能是什么样子(该示例改编自他们的README):

                class ExampleControllerTest < ActionController::TestCase
                  include AssertJson
                
                  def test_my_action
                    get :my_action, :format => 'json'
                    # => @response.body= '{"key":[{"inner_key":"value1"}]}'
                
                    assert_json(@response.body) do
                      has 'key' do
                        has 'inner_key', 'value1'
                      end
                      has_not 'key_not_included'
                    end
                  end
                
                end
                

                您只需在测试中包含AssertJson 模块并使用assert_json 块,您可以在其中检查存在和不存在的键和值的响应。提示:它在README 中不会立即可见,但要检查一个值(例如,如果您的操作只返回一个字符串数组),您可以这样做

                  def test_my_action
                    get :my_action, :format => 'json'
                    # => @response.body= '["value1", "value2"]'
                
                    assert_json(@response.body) do
                      has 'value1'
                      has 'value2'
                      has_not 'value3'
                    end
                  end
                

                【讨论】:

                • 非常好。我刚刚为它贡献了一些小错误修复。
                • 我建议不要使用该 GEM。它有很多问题。
                猜你喜欢
                • 2012-05-30
                • 1970-01-01
                • 2011-04-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多