【问题标题】:How to unit-test a JSON controller?如何对 JSON 控制器进行单元测试?
【发布时间】:2011-11-26 22:29:14
【问题描述】:

这是我的行动:

def my_action
  str = ... # get json str somehow  
  render :json => str
end

这是我的测试:

test "my test" do 
  post(:my_action, {'param' => "value"}    
  assert_response :success
end

我想添加另一个断言,即发出的 JSON 包含一些值。我怎样才能在控制器单元测试中做到这一点,而不是通过解析视图结果?

【问题讨论】:

  • 解析 json 响应不是最简单的方法吗?
  • 我的印象是单元测试实际上并没有调用视图。是这样吗?如果是,我要寻找哪种测试(查看?)
  • 我相信这个问题已经在here 讨论过。您正在做的不是单元测试,而是功能测试。它实际上渲染了视图。

标签: ruby-on-rails ruby


【解决方案1】:

就像人们在上面评论的那样,这将是一个功能测试。

最好的方法可能是发出请求,解析 JSON 响应正文,并将其与预期结果相匹配。

如果我使用 FactoryGirl 在 Rspec 中有 companies_controller

describe "GET 'show'" do

  before(:each) do
    @company = Factory(:company)
    get 'show', :format => :json, :id => @company.id
  end

  it "should be successful" do
     response.should be_success
  end

  it "should return the correct company when correct id is passed" do
    body = JSON.parse(response.body)
    body["id"].should == @company.id
  end

end

您可以用同样的方法测试其他属性。另外,我通常有 invalid 上下文,我会尝试传递无效参数。

【讨论】:

  • FactoryGirl 不需要模型,所以在您的示例中,您必须有“公司”的模型?
【解决方案2】:

使用 Rails 的内置功能测试:

require 'test_helper'

class ZombiesControllerTest < ActionController::TestCase

  setup do
    @request.headers['Accept'] = Mime::JSON
    @request.headers['Content-Type'] = Mime::JSON.to_s

  end

  test "should post my action" do
    post :my_action, { 'param' => "value" }, :format => "json"
    assert_response :success
    body = JSON.parse(response.body)
    assert_equal "Some returned value", body["str"]
  end

end

【讨论】:

  • 当您有一个根据不同块返回不同数据的 respond_to 函数时,此方法有效。在上面的示例中,海报定义了将 REQUEST 发送到控制器的方法,以触发正确的respond_to阻止。
【解决方案3】:

这个控制器测试对我使用 Minitest 和 Rails 4.2.4 效果很好:

require 'test_helper'

class ThingsControllerTest < ActionController::TestCase

  test "should successfully create a new thing" do
    assert_difference 'Thing.count' do
      @request.headers["Accept"] = "application/json"

      post(:create, {thing: {name: "My Thing"}})
    end

    assert_response :success

    json_response = JSON.parse(@response.body)
    assert_equal json_response["name"], "My Thing"
  end

end

这以集成测试的形式起作用。

require 'test_helper'

class ThingsRequestsTest < ActionDispatch::IntegrationTest

  test "creates new thing" do
    assert_difference 'Thing.count' do
      post("/things.json", {thing: { name: "My Thing"}})
    end

    assert_equal 201, status

    json_response = JSON.parse(@response.body)
    assert_equal json_response["name"], "My Thing"
  end

end

老实说,试图将句法细微差别直接从一种测试类型保留到另一种测试是很奇怪的。

【讨论】:

  • headers["Accept"] = "application/json" 在 Rails 6 中为我工作。
  • 在 Rails 6 中,我们可以使用 response.parsed_body 代替 JSON.parse(response.body)
【解决方案4】:

如果我使用 Rails 团队现在提供的 Jbuilder gem,我的处理方法会略有不同。 (这种方法适用于将JSONXML 呈现为视图的其他gem。)我更喜欢单元测试而不是功能测试,因为它们可以更快一些。使用 Jbuilder,您可以将大部分测试转换为单元测试。

是的,您仍然对控制器进行了功能测试,但测试很少,而且它们不解析 JSON。功能测试只测试控制器逻辑,而不是渲染的 JSON。有效请求的功能测试可能会断言以下 (RSpec):

  assert_response :success
  expect(response).to render_template(:show)
  expect(assigns(:item).id).to eq(expected_item.id)

我只是验证它是否成功,它呈现模板,并将项目传递给模板。至此,视图拥有了进行正确渲染所需的信息。

现在测试由单元测试 Jbuilder 视图呈现的 JSON。

describe 'api/v1/items/show.json.jbuilder' do
  it 'includes foo' do
    assign(:item, account.build(foo: 'bar'))

    render

    json = JSON.parse(rendered)
    expect(json['item']['foo']).to eq('bar')
  end

  # A bunch of other JSON tests...

【讨论】:

    猜你喜欢
    • 2012-04-24
    • 2018-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    相关资源
    最近更新 更多